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,58 @@
1
+ require 'hubspot/utils'
2
+
3
+ module Hubspot
4
+ #
5
+ # HubSpot Deals API
6
+ #
7
+ # {http://developers.hubspot.com/docs/methods/deal-pipelines/overview}
8
+ #
9
+ class DealPipeline
10
+ PIPELINES_PATH = "/deals/v1/pipelines"
11
+ PIPELINE_PATH = "/deals/v1/pipelines/:pipeline_id"
12
+
13
+ attr_reader :active
14
+ attr_reader :display_order
15
+ attr_reader :label
16
+ attr_reader :pipeline_id
17
+ attr_reader :stages
18
+
19
+ def initialize(response_hash)
20
+ @active = response_hash["active"]
21
+ @display_order = response_hash["displayOrder"]
22
+ @label = response_hash["label"]
23
+ @pipeline_id = response_hash["pipelineId"]
24
+ @stages = response_hash["stages"]
25
+ end
26
+
27
+ class << self
28
+ def find(pipeline_id)
29
+ response = Hubspot::Connection.get_json(PIPELINE_PATH, { pipeline_id: pipeline_id })
30
+ new(response)
31
+ end
32
+
33
+ def all
34
+ response = Hubspot::Connection.get_json(PIPELINES_PATH, {})
35
+ response.map { |p| new(p) }
36
+ end
37
+
38
+ # Creates a DealPipeline
39
+ # {https://developers.hubspot.com/docs/methods/deal-pipelines/create-deal-pipeline}
40
+ # @return [Hubspot::PipeLine] Company record
41
+ def create!(post_data={})
42
+ response = Hubspot::Connection.post_json(PIPELINES_PATH, params: {}, body: post_data)
43
+ new(response)
44
+ end
45
+ end
46
+
47
+ # Destroys deal_pipeline
48
+ # {http://developers.hubspot.com/docs/methods/companies/delete_company}
49
+ # @return [TrueClass] true
50
+ def destroy!
51
+ Hubspot::Connection.delete_json(PIPELINE_PATH, pipeline_id: @pipeline_id)
52
+ end
53
+
54
+ def [](stage)
55
+ @stages[stage]
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,59 @@
1
+ module Hubspot
2
+ class DealProperties < Properties
3
+
4
+ ALL_PROPERTIES_PATH = '/deals/v1/properties'
5
+ ALL_GROUPS_PATH = '/deals/v1/groups'
6
+ CREATE_PROPERTY_PATH = '/deals/v1/properties/'
7
+ UPDATE_PROPERTY_PATH = '/deals/v1/properties/named/:property_name'
8
+ DELETE_PROPERTY_PATH = '/deals/v1/properties/named/:property_name'
9
+ CREATE_GROUP_PATH = '/deals/v1/groups/'
10
+ UPDATE_GROUP_PATH = '/deals/v1/groups/named/:group_name'
11
+ DELETE_GROUP_PATH = '/deals/v1/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,7 @@
1
+ module Hubspot
2
+ class Deprecator
3
+ def self.build(version: "1.0")
4
+ ActiveSupport::Deprecation.new(version, "hubspot-api-ruby")
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,222 @@
1
+ require 'hubspot/utils'
2
+
3
+ module Hubspot
4
+ #
5
+ # HubSpot Engagements API
6
+ #
7
+ # {http://developers.hubspot.com/docs/methods/engagements/create_engagement}
8
+ #
9
+ class Engagement
10
+ ALL_ENGAGEMENTS_PATH = '/engagements/v1/engagements/paged'
11
+ RECENT_ENGAGEMENT_PATH = '/engagements/v1/engagements/recent/modified'
12
+ CREATE_ENGAGMEMENT_PATH = '/engagements/v1/engagements'
13
+ ENGAGEMENT_PATH = '/engagements/v1/engagements/:engagement_id'
14
+ ASSOCIATE_ENGAGEMENT_PATH = '/engagements/v1/engagements/:engagement_id/associations/:object_type/:object_vid'
15
+ GET_ASSOCIATED_ENGAGEMENTS = '/engagements/v1/engagements/associated/:objectType/:objectId/paged'
16
+
17
+ attr_reader :id
18
+ attr_reader :engagement
19
+ attr_reader :associations
20
+ attr_reader :attachments
21
+ attr_reader :metadata
22
+
23
+ def initialize(response_hash)
24
+
25
+ @engagement = response_hash["engagement"]
26
+ @associations = response_hash["associations"]
27
+ @attachments = response_hash["attachments"]
28
+ @metadata = response_hash["metadata"]
29
+ @id = engagement["id"]
30
+ end
31
+
32
+ class << self
33
+ def create!(params={})
34
+ response = Hubspot::Connection.post_json(CREATE_ENGAGMEMENT_PATH, params: {}, body: params )
35
+ new(HashWithIndifferentAccess.new(response))
36
+ end
37
+
38
+ def find(engagement_id)
39
+ begin
40
+ response = Hubspot::Connection.get_json(ENGAGEMENT_PATH, { engagement_id: engagement_id })
41
+ response ? new(HashWithIndifferentAccess.new(response)) : nil
42
+ rescue Hubspot::RequestError => ex
43
+ if ex.response.code == 404
44
+ return nil
45
+ else
46
+ raise ex
47
+ end
48
+ end
49
+ end
50
+
51
+ def all(opts = {})
52
+ path = ALL_ENGAGEMENTS_PATH
53
+
54
+ response = Hubspot::Connection.get_json(path, opts)
55
+
56
+ result = {}
57
+ result['engagements'] = response['results'].map { |d| new(d) }
58
+ result['offset'] = response['offset']
59
+ result['hasMore'] = response['hasMore']
60
+ return result
61
+ end
62
+
63
+ def recent(opts = {})
64
+ path = RECENT_ENGAGEMENT_PATH
65
+
66
+ response = Hubspot::Connection.get_json(path, opts)
67
+
68
+ result = {}
69
+ result['engagements'] = response['results'].map { |d| new(d) }
70
+ result['offset'] = response['offset']
71
+ result['hasMore'] = response['hasMore']
72
+ result
73
+ end
74
+
75
+ def find_by_company(company_id)
76
+ find_by_association company_id, 'COMPANY'
77
+ end
78
+
79
+ def find_by_contact(contact_id)
80
+ find_by_association contact_id, 'CONTACT'
81
+ end
82
+
83
+ def find_by_association(association_id, association_type)
84
+ path = GET_ASSOCIATED_ENGAGEMENTS
85
+ params = { objectType: association_type, objectId: association_id }
86
+ raise Hubspot::InvalidParams, 'expecting Integer parameter' unless association_id.try(:is_a?, Integer)
87
+ raise Hubspot::InvalidParams, 'expecting String parameter' unless association_type.try(:is_a?, String)
88
+
89
+ engagements = []
90
+ begin
91
+ response = Hubspot::Connection.get_json(path, params)
92
+ engagements = response["results"].try(:map) { |engagement| new(engagement) }
93
+ rescue => e
94
+ raise e unless e.message =~ /not found/
95
+ end
96
+ engagements
97
+ end
98
+
99
+ # Associates an engagement with an object
100
+ # {https://developers.hubspot.com/docs/methods/engagements/associate_engagement}
101
+ # @param engagement_id [int] id of the engagement to associate
102
+ # @param object_type [string] one of contact, company, or deal
103
+ # @param object_vid [int] id of the contact, company, or deal to associate
104
+ def associate!(engagement_id, object_type, object_vid)
105
+ Hubspot::Connection.put_json(ASSOCIATE_ENGAGEMENT_PATH,
106
+ params: {
107
+ engagement_id: engagement_id,
108
+ object_type: object_type,
109
+ object_vid: object_vid
110
+ })
111
+ end
112
+ end
113
+
114
+ # Archives the engagement in hubspot
115
+ # {http://developers.hubspot.com/docs/methods/engagements/delete-engagement}
116
+ # @return [TrueClass] true
117
+ def destroy!
118
+ Hubspot::Connection.delete_json(ENGAGEMENT_PATH, {engagement_id: id})
119
+ @destroyed = true
120
+ end
121
+
122
+ def destroyed?
123
+ !!@destroyed
124
+ end
125
+
126
+ def [](property)
127
+ @properties[property]
128
+ end
129
+
130
+ # Updates the properties of an engagement
131
+ # {http://developers.hubspot.com/docs/methods/engagements/update_engagement}
132
+ # @param params [Hash] hash of properties to update
133
+ # @return [Hubspot::Engagement] self
134
+ def update!(params)
135
+ data = {
136
+ engagement: params[:engagement] || engagement,
137
+ associations: params[:associations] || associations,
138
+ attachments: params[:attachments] || attachments,
139
+ metadata: params[:metadata] || metadata
140
+ }
141
+
142
+ Hubspot::Connection.put_json(ENGAGEMENT_PATH, params: { engagement_id: id }, body: data)
143
+ self
144
+ end
145
+ end
146
+
147
+ class EngagementNote < Engagement
148
+ def body
149
+ metadata['body']
150
+ end
151
+
152
+ def contact_ids
153
+ associations['contactIds']
154
+ end
155
+
156
+ class << self
157
+ def create!(contact_id, note_body, owner_id = nil, deal_id = nil)
158
+ data = {
159
+ engagement: {
160
+ type: 'NOTE'
161
+ },
162
+ associations: {
163
+ contactIds: [contact_id]
164
+ },
165
+ metadata: {
166
+ body: note_body
167
+ }
168
+ }
169
+
170
+ # if the owner id has been provided, append it to the engagement
171
+ data[:engagement][:owner_id] = owner_id if owner_id
172
+ # if the deal id has been provided, associate the note with the deal
173
+ data[:associations][:dealIds] = [deal_id] if deal_id
174
+
175
+ super(data)
176
+ end
177
+ end
178
+ end
179
+
180
+ class EngagementCall < Engagement
181
+ def body
182
+ metadata['body']
183
+ end
184
+
185
+ def contact_ids
186
+ associations['contactIds']
187
+ end
188
+
189
+ def company_ids
190
+ associations['companyIds']
191
+ end
192
+
193
+ def deal_ids
194
+ associations['dealIds']
195
+ end
196
+
197
+ class << self
198
+ def create!(contact_vid, body, duration, owner_id = nil, deal_id = nil, status = 'COMPLETED', time = nil)
199
+ data = {
200
+ engagement: {
201
+ type: 'CALL'
202
+ },
203
+ associations: {
204
+ contactIds: [contact_vid],
205
+ dealIds: [deal_id],
206
+ ownerIds: [owner_id]
207
+ },
208
+ metadata: {
209
+ body: body,
210
+ status: status,
211
+ durationMilliseconds: duration
212
+ }
213
+ }
214
+
215
+ data[:engagement][:timestamp] = (time.to_i) * 1000 if time
216
+ data[:engagement][:owner_id] = owner_id if owner_id
217
+
218
+ super(data)
219
+ end
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,21 @@
1
+ require 'hubspot/utils'
2
+
3
+ module Hubspot
4
+ #
5
+ # HubSpot Events HTTP API
6
+ #
7
+ # {https://developers.hubspot.com/docs/methods/enterprise_events/http_api}
8
+ #
9
+ class Event
10
+ POST_EVENT_PATH = '/v1/event'
11
+
12
+ class << self
13
+ def trigger(event_id, email, options = {})
14
+ default_params = { _n: event_id, _a: Hubspot::Config.portal_id, email: email }
15
+ options[:params] = default_params.merge(options[:params] || {})
16
+
17
+ Hubspot::EventConnection.trigger(POST_EVENT_PATH, options).success?
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ module Hubspot
2
+ class RequestError < StandardError
3
+ attr_accessor :response
4
+
5
+ def initialize(response, message=nil)
6
+ message += "\n" if message
7
+ me = super("#{message}Response body: #{response.body}",)
8
+ me.response = response
9
+ return me
10
+ end
11
+ end
12
+
13
+ class ConfigurationError < StandardError; end
14
+ class MissingInterpolation < StandardError; end
15
+ class ContactExistsError < RequestError; end
16
+ class InvalidParams < StandardError; end
17
+ class ApiError < StandardError; end
18
+ end
@@ -0,0 +1,38 @@
1
+ require 'hubspot/utils'
2
+ require 'base64'
3
+ require 'pp'
4
+
5
+ module Hubspot
6
+ #
7
+ # HubSpot Files API
8
+ #
9
+ # {https://developers.hubspot.com/docs/methods/files/post_files}
10
+ #
11
+ class File
12
+ GET_FILE_PATH = "/filemanager/api/v2/files/:file_id"
13
+ DELETE_FILE_PATH = "/filemanager/api/v2/files/:file_id/full-delete"
14
+ LIST_FILE_PATH = "/filemanager/api/v2/files"
15
+
16
+ attr_reader :id
17
+ attr_reader :properties
18
+
19
+ def initialize(response_hash)
20
+ @id = response_hash["id"]
21
+ @properties = response_hash
22
+ end
23
+
24
+ class << self
25
+ def find_by_id(file_id)
26
+ response = Hubspot::Connection.get_json(GET_FILE_PATH, { file_id: file_id })
27
+ new(response)
28
+ end
29
+ end
30
+
31
+ # Permanently delete a file and all related data and thumbnails from file manager.
32
+ # {https://developers.hubspot.com/docs/methods/files/hard_delete_file_and_associated_objects}
33
+ def destroy!
34
+ Hubspot::Connection.post_json(DELETE_FILE_PATH, params: {file_id: id})
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,95 @@
1
+ module Hubspot
2
+ #
3
+ # HubSpot Form API
4
+ #
5
+ # {https://developers.hubspot.com/docs/methods/forms/forms_overview}
6
+ #
7
+ class Form
8
+ FORMS_PATH = '/forms/v2/forms' # '/contacts/v1/forms'
9
+ FORM_PATH = '/forms/v2/forms/:form_guid' # '/contacts/v1/forms/:form_guid'
10
+ FIELDS_PATH = '/forms/v2/fields/:form_guid' # '/contacts/v1/fields/:form_guid'
11
+ FIELD_PATH = FIELDS_PATH + '/:field_name'
12
+ SUBMIT_DATA_PATH = '/uploads/form/v2/:portal_id/:form_guid'
13
+
14
+ class << self
15
+ # {https://developers.hubspot.com/docs/methods/forms/create_form}
16
+ def create!(opts={})
17
+ response = Hubspot::Connection.post_json(FORMS_PATH, params: {}, body: opts)
18
+ new(response)
19
+ end
20
+
21
+ def all
22
+ response = Hubspot::Connection.get_json(FORMS_PATH, {})
23
+ response.map { |f| new(f) }
24
+ end
25
+
26
+ # {https://developers.hubspot.com/docs/methods/forms/get_form}
27
+ def find(guid)
28
+ response = Hubspot::Connection.get_json(FORM_PATH, { form_guid: guid })
29
+ new(response)
30
+ end
31
+ end
32
+
33
+ attr_reader :guid
34
+ attr_reader :fields
35
+ attr_reader :properties
36
+
37
+ def initialize(hash)
38
+ self.send(:assign_properties, hash)
39
+ end
40
+
41
+ # {https://developers.hubspot.com/docs/methods/forms/get_fields}
42
+ # {https://developers.hubspot.com/docs/methods/forms/get_field}
43
+ def fields(opts={})
44
+ bypass_cache = opts.delete(:bypass_cache) { false }
45
+ field_name = opts.delete(:only) { nil }
46
+
47
+ if field_name
48
+ field_name = field_name.to_s
49
+ if bypass_cache || @fields.nil? || @fields.empty?
50
+ response = Hubspot::Connection.get_json(FIELD_PATH, { form_guid: @guid, field_name: field_name })
51
+ response
52
+ else
53
+ @fields.detect { |f| f['name'] == field_name }
54
+ end
55
+ else
56
+ if bypass_cache || @fields.nil? || @fields.empty?
57
+ response = Hubspot::Connection.get_json(FIELDS_PATH, { form_guid: @guid })
58
+ @fields = response
59
+ end
60
+ @fields
61
+ end
62
+ end
63
+
64
+ # {https://developers.hubspot.com/docs/methods/forms/submit_form}
65
+ def submit(opts={})
66
+ response = Hubspot::FormsConnection.submit(SUBMIT_DATA_PATH, params: { form_guid: @guid }, body: opts)
67
+ [204, 302, 200].include?(response.code)
68
+ end
69
+
70
+ # {https://developers.hubspot.com/docs/methods/forms/update_form}
71
+ def update!(opts={})
72
+ response = Hubspot::Connection.post_json(FORM_PATH, params: { form_guid: @guid }, body: opts)
73
+ self.send(:assign_properties, response)
74
+ self
75
+ end
76
+
77
+ # {https://developers.hubspot.com/docs/methods/forms/delete_form}
78
+ def destroy!
79
+ response = Hubspot::Connection.delete_json(FORM_PATH, { form_guid: @guid })
80
+ @destroyed = (response.code == 204)
81
+ end
82
+
83
+ def destroyed?
84
+ !!@destroyed
85
+ end
86
+
87
+ private
88
+
89
+ def assign_properties(hash)
90
+ @guid = hash['guid']
91
+ @fields = (hash['formFieldGroups'] || []).inject([]) { |result, fg| result | fg['fields'] }
92
+ @properties = hash
93
+ end
94
+ end
95
+ end