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
data/lib/hubspot/blog.rb
ADDED
@@ -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
|