hubspot-api-ruby 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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,98 @@
1
+ module Hubspot
2
+ #
3
+ # HubSpot Contacts API
4
+ #
5
+ class Blog
6
+ BLOG_LIST_PATH = "/content/api/v2/blogs"
7
+ BLOG_POSTS_PATH = "/content/api/v2/blog-posts"
8
+ GET_BLOG_BY_ID_PATH = "/content/api/v2/blogs/:blog_id"
9
+
10
+ class << self
11
+ # Lists the blogs
12
+ # {https://developers.hubspot.com/docs/methods/blogv2/get_blogs}
13
+ # No param filtering is currently implemented
14
+ # @return [Hubspot::Blog] the first 20 blogs or empty_array
15
+ def list
16
+ response = Hubspot::Connection.get_json(BLOG_LIST_PATH, {})
17
+ response['objects'].map { |b| new(b) }
18
+ end
19
+
20
+ # Finds a specific blog by its ID
21
+ # {https://developers.hubspot.com/docs/methods/blogv2/get_blogs_blog_id}
22
+ # @return Hubspot::Blog
23
+ def find_by_id(id)
24
+ response = Hubspot::Connection.get_json(GET_BLOG_BY_ID_PATH, { blog_id: id })
25
+ new(response)
26
+ end
27
+ end
28
+
29
+ attr_reader :properties
30
+
31
+ def initialize(response_hash)
32
+ @properties = response_hash #no need to parse anything, we have properties
33
+ end
34
+
35
+ def [](property)
36
+ @properties[property]
37
+ end
38
+
39
+
40
+ # Returns the posts for this blog instance.
41
+ # defaults to returning the last 2 months worth of published blog posts
42
+ # in date descending order (i.e. most recent first)
43
+ # {https://developers.hubspot.com/docs/methods/blogv2/get_blog_posts}
44
+ # @return [Hubspot::BlogPost]
45
+ def posts(params = {})
46
+ default_params = {
47
+ content_group_id: self["id"],
48
+ order_by: '-created',
49
+ created__gt: Time.now - 2.month,
50
+ state: 'PUBLISHED'
51
+ }
52
+ raise Hubspot::InvalidParams.new('params must be passed as a hash') unless params.is_a?(Hash)
53
+ params = default_params.merge(params)
54
+
55
+ raise Hubspot::InvalidParams.new('State parameter was invalid') unless [false, 'PUBLISHED', 'DRAFT'].include?(params[:state])
56
+ params.each { |k, v| params.delete(k) if v == false }
57
+
58
+ response = Hubspot::Connection.get_json(BLOG_POSTS_PATH, params)
59
+ response['objects'].map { |p| BlogPost.new(p) }
60
+ end
61
+ end
62
+
63
+ class BlogPost
64
+ GET_BLOG_POST_BY_ID_PATH = "/content/api/v2/blog-posts/:blog_post_id"
65
+
66
+ # Returns a specific blog post by ID
67
+ # {https://developers.hubspot.com/docs/methods/blogv2/get_blog_posts_blog_post_id}
68
+ # @return Hubspot::BlogPost
69
+ def self.find_by_blog_post_id(id)
70
+ response = Hubspot::Connection.get_json(GET_BLOG_POST_BY_ID_PATH, { blog_post_id: id })
71
+ new(response)
72
+ end
73
+
74
+ def initialize(response_hash)
75
+ @properties = response_hash #no need to parse anything, we have properties
76
+ end
77
+
78
+ def [](property)
79
+ @properties[property]
80
+ end
81
+
82
+ def created_at
83
+ Time.at(@properties['created'] / 1000)
84
+ end
85
+
86
+ def topics
87
+ @topics ||= begin
88
+ if @properties['topic_ids'].empty?
89
+ []
90
+ else
91
+ @properties['topic_ids'].map do |topic_id|
92
+ Hubspot::Topic.find_by_topic_id(topic_id)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,41 @@
1
+ class Hubspot::Collection
2
+ def initialize(opts = {}, &block)
3
+ @options = opts
4
+ @fetch_proc = block
5
+ fetch
6
+ end
7
+
8
+ def refresh
9
+ fetch
10
+ self
11
+ end
12
+
13
+ def resources
14
+ @resources
15
+ end
16
+
17
+ def update_all(opts = {})
18
+ return true if empty?
19
+
20
+ # This assumes that all resources are the same type
21
+ resource_class = resources.first.class
22
+ unless resource_class.respond_to?(:batch_update)
23
+ raise "#{resource_class} does not support bulk update"
24
+ end
25
+
26
+ resource_class.batch_update(resources, opts)
27
+ end
28
+
29
+ protected
30
+ def fetch
31
+ @resources = @fetch_proc.call(@options)
32
+ end
33
+
34
+ def respond_to_missing?(name, include_private = false)
35
+ @resources.respond_to?(name, include_private)
36
+ end
37
+
38
+ def method_missing(method, *args, &block)
39
+ @resources.public_send(method, *args, &block)
40
+ end
41
+ end
@@ -0,0 +1,160 @@
1
+ class Hubspot::Company < Hubspot::Resource
2
+ self.id_field = "companyId"
3
+ self.property_name_field = "name"
4
+
5
+ ADD_CONTACT_PATH = '/companies/v2/companies/:id/contacts/:contact_id'
6
+ ALL_PATH = '/companies/v2/companies/paged'
7
+ BATCH_UPDATE_PATH = '/companies/v1/batch-async/update'
8
+ CONTACTS_PATH = '/companies/v2/companies/:id/contacts'
9
+ CONTACT_IDS_PATH = '/companies/v2/companies/:id/vids'
10
+ CREATE_PATH = '/companies/v2/companies/'
11
+ DELETE_PATH = '/companies/v2/companies/:id'
12
+ FIND_PATH = '/companies/v2/companies/:id'
13
+ RECENTLY_CREATED_PATH = '/companies/v2/companies/recent/created'
14
+ RECENTLY_MODIFIED_PATH = '/companies/v2/companies/recent/modified'
15
+ REMOVE_CONTACT_PATH = '/companies/v2/companies/:id/contacts/:contact_id'
16
+ SEARCH_DOMAIN_PATH = '/companies/v2/domains/:domain/companies'
17
+ UPDATE_PATH = '/companies/v2/companies/:id'
18
+
19
+ class << self
20
+ def all(opts = {})
21
+ Hubspot::PagedCollection.new(opts) do |options, offset, limit|
22
+ response = Hubspot::Connection.get_json(
23
+ ALL_PATH,
24
+ options.merge(offset: offset, limit: limit)
25
+ )
26
+
27
+ companies = response["companies"].map { |result| from_result(result) }
28
+
29
+ [companies, response["offset"], response["has-more"]]
30
+ end
31
+ end
32
+
33
+ def search_domain(domain, opts = {})
34
+ Hubspot::PagedCollection.new(opts) do |options, offset, limit|
35
+ request = {
36
+ "limit" => limit,
37
+ "requestOptions" => options,
38
+ "offset" => {
39
+ "isPrimary" => true,
40
+ "companyId" => offset
41
+ }
42
+ }
43
+
44
+ response = Hubspot::Connection.post_json(
45
+ SEARCH_DOMAIN_PATH,
46
+ params: { domain: domain },
47
+ body: request
48
+ )
49
+
50
+ companies = response["results"].map { |result| from_result(result) }
51
+
52
+ [companies, response["offset"]["companyId"], response["hasMore"]]
53
+ end
54
+ end
55
+
56
+ def recently_created(opts = {})
57
+ Hubspot::PagedCollection.new(opts) do |options, offset, limit|
58
+ response = Hubspot::Connection.get_json(
59
+ RECENTLY_CREATED_PATH,
60
+ {offset: offset, count: limit}
61
+ )
62
+
63
+ companies = response["results"].map { |result| from_result(result) }
64
+
65
+ [companies, response["offset"], response["hasMore"]]
66
+ end
67
+ end
68
+
69
+ def recently_modified(opts = {})
70
+ Hubspot::PagedCollection.new(opts) do |options, offset, limit|
71
+ response = Hubspot::Connection.get_json(
72
+ RECENTLY_MODIFIED_PATH,
73
+ {offset: offset, count: limit}
74
+ )
75
+
76
+ companies = response["results"].map { |result| from_result(result) }
77
+
78
+ [companies, response["offset"], response["hasMore"]]
79
+ end
80
+ end
81
+
82
+ def add_contact(id, contact_id)
83
+ Hubspot::Connection.put_json(
84
+ ADD_CONTACT_PATH,
85
+ params: { id: id, contact_id: contact_id }
86
+ )
87
+ true
88
+ end
89
+
90
+ def remove_contact(id, contact_id)
91
+ Hubspot::Connection.delete_json(
92
+ REMOVE_CONTACT_PATH,
93
+ { id: id, contact_id: contact_id }
94
+ )
95
+
96
+ true
97
+ end
98
+
99
+ def batch_update(companies, opts = {})
100
+ request = companies.map do |company|
101
+ # Use the specified options or update with the changes
102
+ changes = opts.empty? ? company.changes : opts
103
+
104
+ unless changes.empty?
105
+ {
106
+ "objectId" => company.id,
107
+ "properties" => changes.map { |k, v| { "name" => k, "value" => v } }
108
+ }
109
+ end
110
+ end
111
+
112
+ # Remove any objects without changes and return if there is nothing to update
113
+ request.compact!
114
+ return true if request.empty?
115
+
116
+ Hubspot::Connection.post_json(
117
+ BATCH_UPDATE_PATH,
118
+ params: {},
119
+ body: request
120
+ )
121
+
122
+ true
123
+ end
124
+ end
125
+
126
+ def contacts(opts = {})
127
+ Hubspot::PagedCollection.new(opts) do |options, offset, limit|
128
+ response = Hubspot::Connection.get_json(
129
+ CONTACTS_PATH,
130
+ {"id" => @id, "vidOffset" => offset, "count" => limit}
131
+ )
132
+
133
+ contacts = response["contacts"].map do |result|
134
+ result["properties"] = Hubspot::Utils.properties_array_to_hash(result["properties"])
135
+ Hubspot::Contact.from_result(result)
136
+ end
137
+
138
+ [contacts, response["vidOffset"], response["hasMore"]]
139
+ end
140
+ end
141
+
142
+ def contact_ids(opts = {})
143
+ Hubspot::PagedCollection.new(opts) do |options, offset, limit|
144
+ response = Hubspot::Connection.get_json(
145
+ CONTACT_IDS_PATH,
146
+ {"id" => @id, "vidOffset" => offset, "count" => limit}
147
+ )
148
+
149
+ [response["vids"], response["vidOffset"], response["hasMore"]]
150
+ end
151
+ end
152
+
153
+ def add_contact(contact)
154
+ self.class.add_contact(@id, contact.to_i)
155
+ end
156
+
157
+ def remove_contact(contact)
158
+ self.class.remove_contact(@id, contact.to_i)
159
+ end
160
+ end
@@ -0,0 +1,59 @@
1
+ module Hubspot
2
+ class CompanyProperties < Properties
3
+
4
+ ALL_PROPERTIES_PATH = "/properties/v1/companies/properties"
5
+ ALL_GROUPS_PATH = "/properties/v1/companies/groups"
6
+ CREATE_PROPERTY_PATH = "/properties/v1/companies/properties"
7
+ UPDATE_PROPERTY_PATH = "/properties/v1/companies/properties/named/:property_name"
8
+ DELETE_PROPERTY_PATH = "/properties/v1/companies/properties/named/:property_name"
9
+ CREATE_GROUP_PATH = "/properties/v1/companies/groups"
10
+ UPDATE_GROUP_PATH = "/properties/v1/companies/groups/named/:group_name"
11
+ DELETE_GROUP_PATH = "/properties/v1/companies/groups/named/:group_name"
12
+
13
+ class << self
14
+ def add_default_parameters(opts={})
15
+ superclass.add_default_parameters(opts)
16
+ end
17
+
18
+ def all(opts={}, filter={})
19
+ superclass.all(ALL_PROPERTIES_PATH, opts, filter)
20
+ end
21
+
22
+ def groups(opts={}, filter={})
23
+ superclass.groups(ALL_GROUPS_PATH, opts, filter)
24
+ end
25
+
26
+ def create!(params={})
27
+ superclass.create!(CREATE_PROPERTY_PATH, params)
28
+ end
29
+
30
+ def update!(property_name, params={})
31
+ superclass.update!(UPDATE_PROPERTY_PATH, property_name, params)
32
+ end
33
+
34
+ def delete!(property_name)
35
+ superclass.delete!(DELETE_PROPERTY_PATH, property_name)
36
+ end
37
+
38
+ def create_group!(params={})
39
+ superclass.create_group!(CREATE_GROUP_PATH, params)
40
+ end
41
+
42
+ def update_group!(group_name, params={})
43
+ superclass.update_group!(UPDATE_GROUP_PATH, group_name, params)
44
+ end
45
+
46
+ def delete_group!(group_name)
47
+ superclass.delete_group!(DELETE_GROUP_PATH, group_name)
48
+ end
49
+
50
+ def same?(src, dst)
51
+ superclass.same?(src, dst)
52
+ end
53
+
54
+ def valid_params(params)
55
+ superclass.valid_params(params)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,63 @@
1
+ require 'logger'
2
+ require 'hubspot/connection'
3
+
4
+ module Hubspot
5
+ class Config
6
+ CONFIG_KEYS = [
7
+ :hapikey, :base_url, :portal_id, :logger, :access_token, :client_id,
8
+ :client_secret, :redirect_uri, :read_timeout, :open_timeout
9
+ ]
10
+ DEFAULT_LOGGER = Logger.new(nil)
11
+ DEFAULT_BASE_URL = "https://api.hubapi.com".freeze
12
+
13
+ class << self
14
+ attr_accessor *CONFIG_KEYS
15
+
16
+ def configure(config)
17
+ config.stringify_keys!
18
+ @hapikey = config["hapikey"]
19
+ @base_url = config["base_url"] || DEFAULT_BASE_URL
20
+ @portal_id = config["portal_id"]
21
+ @logger = config["logger"] || DEFAULT_LOGGER
22
+ @access_token = config["access_token"]
23
+ @client_id = config["client_id"] if config["client_id"].present?
24
+ @client_secret = config["client_secret"] if config["client_secret"].present?
25
+ @redirect_uri = config["redirect_uri"] if config["redirect_uri"].present?
26
+ @read_timeout = config['read_timeout'] || config['timeout']
27
+ @open_timeout = config['open_timeout'] || config['timeout']
28
+
29
+ unless authentication_uncertain?
30
+ raise Hubspot::ConfigurationError.new("You must provide either an access_token or an hapikey")
31
+ end
32
+
33
+ if access_token.present?
34
+ Hubspot::Connection.headers("Authorization" => "Bearer #{access_token}")
35
+ end
36
+ self
37
+ end
38
+
39
+ def reset!
40
+ @hapikey = nil
41
+ @base_url = DEFAULT_BASE_URL
42
+ @portal_id = nil
43
+ @logger = DEFAULT_LOGGER
44
+ @access_token = nil
45
+ Hubspot::Connection.headers({})
46
+ end
47
+
48
+ def ensure!(*params)
49
+ params.each do |p|
50
+ raise Hubspot::ConfigurationError.new("'#{p}' not configured") unless instance_variable_get "@#{p}"
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def authentication_uncertain?
57
+ access_token.present? ^ hapikey.present?
58
+ end
59
+ end
60
+
61
+ reset!
62
+ end
63
+ end
@@ -0,0 +1,152 @@
1
+ module Hubspot
2
+ class Connection
3
+ include HTTParty
4
+
5
+ class << self
6
+ def get_json(path, opts)
7
+ url = generate_url(path, opts)
8
+ response = get(url, format: :json, read_timeout: read_timeout(opts), open_timeout: open_timeout(opts))
9
+ log_request_and_response url, response
10
+ handle_response(response)
11
+ end
12
+
13
+ def post_json(path, opts)
14
+ no_parse = opts[:params].delete(:no_parse) { false }
15
+
16
+ url = generate_url(path, opts[:params])
17
+ response = post(
18
+ url,
19
+ body: opts[:body].to_json,
20
+ headers: { 'Content-Type' => 'application/json' },
21
+ format: :json,
22
+ read_timeout: read_timeout(opts),
23
+ open_timeout: open_timeout(opts)
24
+ )
25
+
26
+ log_request_and_response url, response, opts[:body]
27
+ raise(Hubspot::RequestError.new(response)) unless response.success?
28
+
29
+ no_parse ? response : response.parsed_response
30
+ end
31
+
32
+ def put_json(path, options)
33
+ no_parse = options[:params].delete(:no_parse) { false }
34
+ url = generate_url(path, options[:params])
35
+
36
+ response = put(
37
+ url,
38
+ body: options[:body].to_json,
39
+ headers: { "Content-Type" => "application/json" },
40
+ format: :json,
41
+ read_timeout: read_timeout(options),
42
+ open_timeout: open_timeout(options),
43
+ )
44
+
45
+ log_request_and_response(url, response, options[:body])
46
+ raise(Hubspot::RequestError.new(response)) unless response.success?
47
+
48
+ no_parse ? response : response.parsed_response
49
+ end
50
+
51
+ def delete_json(path, opts)
52
+ url = generate_url(path, opts)
53
+ response = delete(url, format: :json, read_timeout: read_timeout(opts), open_timeout: open_timeout(opts))
54
+ log_request_and_response url, response, opts[:body]
55
+ raise(Hubspot::RequestError.new(response)) unless response.success?
56
+ response
57
+ end
58
+
59
+ protected
60
+
61
+ def read_timeout(opts = {})
62
+ opts.delete(:read_timeout) || Hubspot::Config.read_timeout
63
+ end
64
+
65
+ def open_timeout(opts = {})
66
+ opts.delete(:open_timeout) || Hubspot::Config.open_timeout
67
+ end
68
+
69
+ def handle_response(response)
70
+ if response.success?
71
+ response.parsed_response
72
+ else
73
+ raise(Hubspot::RequestError.new(response))
74
+ end
75
+ end
76
+
77
+ def log_request_and_response(uri, response, body=nil)
78
+ Hubspot::Config.logger.info(<<~MSG)
79
+ Hubspot: #{uri}.
80
+ Body: #{body}.
81
+ Response: #{response.code} #{response.body}
82
+ MSG
83
+ end
84
+
85
+ def generate_url(path, params={}, options={})
86
+ if Hubspot::Config.access_token.present?
87
+ options[:hapikey] = false
88
+ else
89
+ Hubspot::Config.ensure! :hapikey
90
+ end
91
+ path = path.clone
92
+ params = params.clone
93
+ base_url = options[:base_url] || Hubspot::Config.base_url
94
+ params["hapikey"] = Hubspot::Config.hapikey unless options[:hapikey] == false
95
+
96
+ if path =~ /:portal_id/
97
+ Hubspot::Config.ensure! :portal_id
98
+ params["portal_id"] = Hubspot::Config.portal_id if path =~ /:portal_id/
99
+ end
100
+
101
+ params.each do |k,v|
102
+ if path.match(":#{k}")
103
+ path.gsub!(":#{k}", CGI.escape(v.to_s))
104
+ params.delete(k)
105
+ end
106
+ end
107
+ raise(Hubspot::MissingInterpolation.new("Interpolation not resolved")) if path =~ /:/
108
+
109
+ query = params.map do |k,v|
110
+ v.is_a?(Array) ? v.map { |value| param_string(k,value) } : param_string(k,v)
111
+ end.join("&")
112
+
113
+ path += path.include?('?') ? '&' : "?" if query.present?
114
+ base_url + path + query
115
+ end
116
+
117
+ # convert into milliseconds since epoch
118
+ def converted_value(value)
119
+ value.is_a?(Time) ? (value.to_i * 1000) : CGI.escape(value.to_s)
120
+ end
121
+
122
+ def param_string(key,value)
123
+ case key
124
+ when /range/
125
+ raise "Value must be a range" unless value.is_a?(Range)
126
+ "#{key}=#{converted_value(value.begin)}&#{key}=#{converted_value(value.end)}"
127
+ when /^batch_(.*)$/
128
+ key = $1.gsub(/(_.)/) { |w| w.last.upcase }
129
+ "#{key}=#{converted_value(value)}"
130
+ else
131
+ "#{key}=#{converted_value(value)}"
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ class FormsConnection < Connection
138
+ follow_redirects true
139
+
140
+ def self.submit(path, opts)
141
+ url = generate_url(path, opts[:params], { base_url: 'https://forms.hubspot.com', hapikey: false })
142
+ post(url, body: opts[:body], headers: { 'Content-Type' => 'application/x-www-form-urlencoded' })
143
+ end
144
+ end
145
+
146
+ class EventConnection < Connection
147
+ def self.trigger(path, opts)
148
+ url = generate_url(path, opts[:params], { base_url: 'https://track.hubspot.com', hapikey: false })
149
+ get(url, body: opts[:body], headers: opts[:headers])
150
+ end
151
+ end
152
+ end