ruby_hubspot_api 0.2.2 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/ruby.yml +4 -4
- data/.gitignore +1 -1
- data/CHANGELOG.md +125 -75
- data/Gemfile.lock +1 -1
- data/README.md +37 -1
- data/lib/hubspot/batch.rb +36 -12
- data/lib/hubspot/contact.rb +63 -0
- data/lib/hubspot/exceptions.rb +1 -0
- data/lib/hubspot/form.rb +5 -3
- data/lib/hubspot/paged_batch.rb +2 -1
- data/lib/hubspot/paged_collection.rb +1 -0
- data/lib/hubspot/resource.rb +77 -35
- data/lib/hubspot/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 62b690c7e426369cd5979c4f6b5b0cec8b5a7620
|
4
|
+
data.tar.gz: 26047bb4aa34d3a91bf6c1aff80b6e3775220f4f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6512174b13fa2ef07740690864c79a6c3593c908c1fed37170ef35bf08ca7ae5ceb9a3e53fe12d53079eb4e4bb47a58801430103b96c9a7b78dfd67eaaabaa5
|
7
|
+
data.tar.gz: 6ea719cb62b217fdbbdb2009d64215369608ba8d3423b59245472fb64fcd957174c5cc28a0cbcbd1f5eaf904260d19e1989da528df913292c139b5f4d893110e
|
data/.github/workflows/ruby.yml
CHANGED
@@ -41,7 +41,7 @@ jobs:
|
|
41
41
|
env:
|
42
42
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
43
43
|
|
44
|
-
- name: Upload coverage to Codacy
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
# - name: Upload coverage to Codacy
|
45
|
+
# run: bash <(curl -Ls https://coverage.codacy.com/get.sh)
|
46
|
+
# env:
|
47
|
+
# CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,89 +1,139 @@
|
|
1
|
-
## v.0.2.0
|
2
1
|
|
3
|
-
|
4
|
-
|
5
|
-
-
|
6
|
-
-
|
7
|
-
-
|
8
|
-
-
|
9
|
-
-
|
10
|
-
-
|
11
|
-
-
|
12
|
-
-
|
13
|
-
-
|
14
|
-
-
|
15
|
-
-
|
16
|
-
-
|
17
|
-
-
|
18
|
-
-
|
19
|
-
-
|
20
|
-
-
|
21
|
-
-
|
22
|
-
- Adds
|
2
|
+
## v0.3.0
|
3
|
+
|
4
|
+
- update gem version in lock file
|
5
|
+
- bump version
|
6
|
+
- use safe navigation for extracting id
|
7
|
+
- make erb explicitly determined by the file exension (.json.erb)
|
8
|
+
- check the env vars before sanitising
|
9
|
+
- Finish specs
|
10
|
+
- Sanitize web mock output
|
11
|
+
- Tests the method missing setter for resource
|
12
|
+
- find_by_token method - uses v1 API
|
13
|
+
- Contact find_by_token spec
|
14
|
+
- Allow ERB in json fixtures
|
15
|
+
- No cov for inspect
|
16
|
+
- Ensure properties are passed as named argument
|
17
|
+
- Update batch spec to check resource_matcher works
|
18
|
+
- Drop cadacy for now
|
19
|
+
- Ignore gem files
|
20
|
+
- Dynamically add a method to batch to allow "resources" to be referred to as the resource_name
|
21
|
+
- Adds validation for resource matcher
|
22
|
+
- Bump the version
|
23
|
+
- Update batch spec
|
24
|
+
- Clear up processing results
|
25
|
+
- Fix find resources
|
26
|
+
- Update the hierarchy to allow more flexibility
|
27
|
+
- Tidy comments
|
28
|
+
- Adds :sparkle: attributes method to a resource
|
29
|
+
- Improve logic of resource matching
|
30
|
+
- Update user model to force specific properties to be retrieved
|
31
|
+
- Add some handling of required properties
|
32
|
+
- Tidy up somer doc comments
|
33
|
+
- adjust some rubocop settings
|
34
|
+
- Yep. Back ported to 2.4
|
35
|
+
- Tidy up documentation of resource
|
36
|
+
- Ensure we always apply the right log_level
|
37
|
+
- Adding Codacy badge
|
38
|
+
- try to upload the coverage results to codacy too
|
39
|
+
- Only apply lcov formatter if running on github
|
40
|
+
- Add property check to the contact spec
|
41
|
+
- Add read-only properties to resource class
|
42
|
+
- Adding lcov format
|
43
|
+
- Using earlier bundler
|
44
|
+
- adding codecov
|
45
|
+
- determine if a Hubspot property is read_only (or by negation updatable)
|
46
|
+
- test on ruby 3.0 too
|
47
|
+
- ignore ruby version file
|
48
|
+
- Use ERB in VCR tests so as to be independent of the env vars
|
49
|
+
- try calling rspec directly
|
50
|
+
- add ruby 2.5 and reduce log output
|
51
|
+
- add plaatforms to Gemfile.lock
|
52
|
+
- fix the github workflow properly
|
53
|
+
- fix the github workflow
|
54
|
+
- Create ruby.yml
|
55
|
+
- More usage clarification
|
56
|
+
- Clarify usage
|
57
|
+
|
58
|
+
## v0.2.0
|
59
|
+
|
60
|
+
- bump version
|
23
61
|
- Update the Readme to add Batch operations
|
24
|
-
-
|
62
|
+
- Adds PagedBatch as pager for batch/read request
|
63
|
+
- Simplify mocked responses in batch spec
|
64
|
+
- Move rate limit handling to the client
|
65
|
+
- add configurable timeouts to requests
|
66
|
+
- Add api client logging spec
|
67
|
+
- Cover the previously nocov'd code
|
68
|
+
- Tidy up resource code
|
69
|
+
- Adds create and archive methods to batches
|
70
|
+
- Add all end points to the batch spec
|
71
|
+
- Ensure keys are stringified
|
72
|
+
- Adds instance method resource_name on resource
|
73
|
+
- Adds a changes? Method on resource
|
74
|
+
- logger.debug the post body and response body
|
75
|
+
- batch implemntation
|
76
|
+
- Borrowing Object#blank? method cos it actually really helps...
|
77
|
+
- batch :sparkle: upsert spec
|
78
|
+
- update lock
|
79
|
+
- describe find_by method
|
80
|
+
- Bump the version again
|
81
|
+
- Get the development dependencies right!
|
25
82
|
|
26
83
|
## v0.1.2
|
27
84
|
|
28
|
-
- initial setup
|
29
|
-
- Setup the configuration block
|
30
|
-
- Adds spec for config
|
31
|
-
- Set the auth headers when access_token configured
|
32
|
-
- Version spec
|
33
|
-
- don't test for client id
|
34
|
-
- Load api client and add exception handler
|
35
|
-
- Initial bases class Resource for api crud
|
36
|
-
- Contact class with spec
|
37
|
-
- Cassettes for contact spec
|
38
|
-
- company model and spec with cassettes
|
39
|
-
- Adds PagedCollection for paged results (list / search)
|
40
|
-
- Adds Hubspot configuration to tests
|
41
|
-
- Update required files
|
42
|
-
- Adds list method to return PagedCollection
|
43
|
-
- Add interface for search
|
44
|
-
- VCR configuration
|
45
|
-
- Contact/search cassette
|
46
|
-
- Sample ENV file for developers
|
47
|
-
- console with configuration if env vars set
|
48
|
-
- Readme file
|
49
|
-
- MIT license
|
50
|
-
- allow connections if vcr_record_mode is on
|
51
|
-
- Fix rubocop config
|
52
|
-
- Update Readme
|
53
|
-
- Log api requests and add interface to set logging
|
54
|
-
- Update exception handing logic and add more exception classes
|
55
|
-
- Test all parts of the config code
|
56
|
-
- Adds user model (aliased to Owner) and specs
|
57
|
-
- Add a configured? method to Hubspot module
|
58
|
-
- Adds a dummy Hubspot::Property class
|
59
|
-
- Update Paged request handling based on method
|
60
|
-
- Adds a find_by mechanism for resources
|
61
|
-
- Use the Hubspot::Property class to return properties for a given resource object
|
62
|
-
- Update the specs for 100% coverage
|
63
|
-
- Update the sample .env file
|
64
|
-
- Reorder and clarify Readme
|
65
|
-
- When we use limit(1) we should only return the object not an array
|
66
|
-
- Test that we only get the properties we ask for or the defaults
|
67
|
-
- Flatten the properties array into a comma separated list
|
68
|
-
- Improve the intialiser
|
69
|
-
- Update the changeling and link in gem spec
|
70
|
-
- adds the version numbers to the gemspec
|
71
|
-
- Fix dependencies
|
72
85
|
- bump version
|
73
|
-
- Fix the Readme
|
74
|
-
- Sure the search param is values where passing an array
|
75
86
|
- update changelog and Gemfile.lock
|
76
|
-
-
|
87
|
+
- Sure the search param is values where passing an array
|
88
|
+
- Fix the Readme
|
77
89
|
|
78
90
|
## v0.1.1
|
79
91
|
|
80
|
-
- Fix the Readme
|
81
|
-
- Sure the search param is values where passing an array
|
82
|
-
- update changelog and Gemfile.lock
|
83
92
|
- bump version
|
93
|
+
- Fix dependencies
|
94
|
+
- adds the version numbers to the gemspec
|
84
95
|
|
85
96
|
## v0.1.0
|
86
97
|
|
87
|
-
-
|
88
|
-
-
|
89
|
-
-
|
98
|
+
- Update the changeling and link in gem spec
|
99
|
+
- Improve the intialiser
|
100
|
+
- Flatten the properties array into a comma separated list
|
101
|
+
- Test that we only get the properties we ask for or the defaults
|
102
|
+
- When we use limit(1) we should only return the object not an array
|
103
|
+
- Reorder and clarify Readme
|
104
|
+
- Update the sample .env file
|
105
|
+
- Update the specs for 100% coverage
|
106
|
+
- Use the Hubspot::Property class to return properties for a given resource object
|
107
|
+
- Adds a find_by mechanism for resources
|
108
|
+
- Update Paged request handling based on method
|
109
|
+
- Adds a dummy Hubspot::Property class
|
110
|
+
- Add a configured? method to Hubspot module
|
111
|
+
- Adds user model (aliased to Owner) and specs
|
112
|
+
- Test all parts of the config code
|
113
|
+
- Update exception handing logic and add more exception classes
|
114
|
+
- Log api requests and add interface to set logging
|
115
|
+
- Update Readme
|
116
|
+
- Fix rubocop config
|
117
|
+
- allow connections if vcr_record_mode is on
|
118
|
+
- MIT license
|
119
|
+
- Readme file
|
120
|
+
- console with configuration if env vars set
|
121
|
+
- Sample ENV file for developers
|
122
|
+
- Contact/search cassette
|
123
|
+
- VCR configuration
|
124
|
+
- Add interface for search
|
125
|
+
- Adds list method to return PagedCollection
|
126
|
+
- Update required files
|
127
|
+
- Adds Hubspot configuration to tests
|
128
|
+
- Adds PagedCollection for paged results (list / search)
|
129
|
+
- company model and spec with cassettes
|
130
|
+
- Cassettes for contact spec
|
131
|
+
- Contact class with spec
|
132
|
+
- Initial bases class Resource for api crud
|
133
|
+
- Load api client and add exception handler
|
134
|
+
- don't test for client id
|
135
|
+
- Version spec
|
136
|
+
- Set the auth headers when access_token configured
|
137
|
+
- Adds spec for config
|
138
|
+
- Setup the configuration block
|
139
|
+
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -120,7 +120,7 @@ puts "Contact: #{contact.firstname} #{contact.lastname}"
|
|
120
120
|
|
121
121
|
### Updating an Existing Object
|
122
122
|
|
123
|
-
To update an existing object, you can either modify the object and call `save`, or use the `update` method specifying the properties you want to update. You can test whether or not the object will need to upload changes to the api by using the changes? method
|
123
|
+
To update an existing object, you can either modify the object and call `save`, or use the `update` method specifying the properties you want to update. You can test whether or not the object will need to upload changes to the api by using the changes? method. If you don't want to check for changes? you can use the method `save!` on the resource which will raise a Hubspot::NothingToDoError if there are no changes.
|
124
124
|
|
125
125
|
Example using `save`:
|
126
126
|
|
@@ -136,6 +136,22 @@ contact.save # true
|
|
136
136
|
contact.changes? # false
|
137
137
|
```
|
138
138
|
|
139
|
+
Example using `save!`:
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
contact = Hubspot::Contact.find(1)
|
143
|
+
|
144
|
+
MyDecorator.new(contact).apply_changes!
|
145
|
+
|
146
|
+
begin
|
147
|
+
contact.save!
|
148
|
+
rescue Hubspot::NothingToDoError = _
|
149
|
+
puts "Nothing changed, cancelled api call"
|
150
|
+
rescue Hubspot::RequestError => e
|
151
|
+
puts "API Error: #{e.message}"
|
152
|
+
end
|
153
|
+
```
|
154
|
+
|
139
155
|
Example using `update`:
|
140
156
|
|
141
157
|
```ruby
|
@@ -274,6 +290,26 @@ end
|
|
274
290
|
- **lte**: Less than or equal to.
|
275
291
|
- **IN**: Matches any of the values in an array.
|
276
292
|
|
293
|
+
#### Searching for empty values (NOT_HAS_PROPERTY)
|
294
|
+
|
295
|
+
Any empty value in your search will be matched using the correect filter in Hubspot
|
296
|
+
|
297
|
+
```ruby
|
298
|
+
# Search for companies with no value for a given field
|
299
|
+
companies = Hubspot::Company.search(query: { client_category: nil }, properties: %w[name number_of_employees])
|
300
|
+
# Request body: {"filterGroups":[{"filters":[{"propertyName":"client_category","operator":"NOT_HAS_PROPERTY"}]}]
|
301
|
+
|
302
|
+
puts "Searching for uncategorised customers"
|
303
|
+
puts ""
|
304
|
+
|
305
|
+
companies.each do |company|
|
306
|
+
category = company_category_by_size(company.number_of_employees)
|
307
|
+
company.update(client_category: category)
|
308
|
+
|
309
|
+
puts " Found: #{company.name} (size #{company.number_of_employees}) - filed under #{category}"
|
310
|
+
end
|
311
|
+
```
|
312
|
+
|
277
313
|
#### Specifying Properties in Search
|
278
314
|
|
279
315
|
When performing a search, you can also specify which properties to return.
|
data/lib/hubspot/batch.rb
CHANGED
@@ -31,6 +31,7 @@ module Hubspot
|
|
31
31
|
CONTACT_LIMIT = 10
|
32
32
|
DEFAULT_LIMIT = 100
|
33
33
|
|
34
|
+
# :nocov:
|
34
35
|
def inspect
|
35
36
|
"#<#{self.class.name} " \
|
36
37
|
"@resource_count=#{@resources.size}, " \
|
@@ -38,16 +39,11 @@ module Hubspot
|
|
38
39
|
"@resource_type=#{@resources.first&.resource_name}, " \
|
39
40
|
"@responses_count=#{@responses.size}>"
|
40
41
|
end
|
42
|
+
# :nocov:
|
41
43
|
|
42
44
|
# rubocop:disable Lint/MissingSuper
|
43
45
|
def initialize(resources = [], id_property: 'id', resource_matcher: nil)
|
44
|
-
|
45
|
-
unless resource_matcher.is_a?(Proc) && resource_matcher.arity == 2
|
46
|
-
raise ArgumentError, 'resource_matcher must be a proc that accepts exactly 2 arguments'
|
47
|
-
end
|
48
|
-
|
49
|
-
@resource_matcher = resource_matcher
|
50
|
-
end
|
46
|
+
validate_resource_matcher(resource_matcher)
|
51
47
|
|
52
48
|
@resources = []
|
53
49
|
@id_property = id_property # Set id_property for the batch (default: 'id')
|
@@ -67,11 +63,23 @@ module Hubspot
|
|
67
63
|
end
|
68
64
|
|
69
65
|
# Upsert method that calls save with upsert action
|
70
|
-
def upsert
|
66
|
+
def upsert(resource_matcher: nil)
|
67
|
+
validate_resource_matcher(resource_matcher)
|
68
|
+
|
71
69
|
validate_upsert_conditions
|
72
70
|
save(action: 'upsert')
|
73
71
|
end
|
74
72
|
|
73
|
+
def validate_resource_matcher(resource_matcher)
|
74
|
+
return if resource_matcher.blank?
|
75
|
+
|
76
|
+
unless resource_matcher.is_a?(Proc) && resource_matcher.arity == 2
|
77
|
+
raise ArgumentError, 'resource_matcher must be a proc that accepts exactly 2 arguments'
|
78
|
+
end
|
79
|
+
|
80
|
+
@resource_matcher = resource_matcher
|
81
|
+
end
|
82
|
+
|
75
83
|
# Archive method
|
76
84
|
def archive
|
77
85
|
save(action: 'archive')
|
@@ -93,15 +101,29 @@ module Hubspot
|
|
93
101
|
end
|
94
102
|
|
95
103
|
def add_resource(resource)
|
96
|
-
if @resources.any?
|
97
|
-
|
104
|
+
if @resources.any?
|
105
|
+
if @resources.first.resource_name != resource.resource_name
|
106
|
+
raise ArgumentError, 'All resources in a batch must be of the same type'
|
107
|
+
end
|
108
|
+
else
|
109
|
+
add_resource_method(resource.resource_name)
|
98
110
|
end
|
99
111
|
|
100
112
|
@resources << resource
|
101
113
|
end
|
102
114
|
|
115
|
+
def any_changes?
|
116
|
+
@resources.any?(&:changes?)
|
117
|
+
end
|
118
|
+
|
103
119
|
private
|
104
120
|
|
121
|
+
def add_resource_method(resource_name)
|
122
|
+
self.class.class_eval do
|
123
|
+
alias_method resource_name.to_sym, :resources
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
105
127
|
# rubocop:disable Metrics/MethodLength
|
106
128
|
def save(action: 'update')
|
107
129
|
@action = action
|
@@ -280,13 +302,15 @@ module Hubspot
|
|
280
302
|
end
|
281
303
|
|
282
304
|
class << self
|
283
|
-
def read(object_class, object_ids = [], id_property: 'id')
|
305
|
+
def read(object_class, object_ids = [], properties: [], id_property: 'id')
|
284
306
|
unless object_class < Hubspot::Resource
|
285
307
|
raise ArgumentError, 'Must be a valid Hubspot resource class'
|
286
308
|
end
|
287
309
|
|
288
310
|
# fetch all the matching resources with paging handled
|
289
|
-
resources = object_class.batch_read(object_ids,
|
311
|
+
resources = object_class.batch_read(object_ids,
|
312
|
+
properties: properties,
|
313
|
+
id_property: id_property).all
|
290
314
|
|
291
315
|
# return instance of Hubspot::Batch with the resources set
|
292
316
|
new(resources, id_property: id_property)
|
data/lib/hubspot/contact.rb
CHANGED
@@ -2,5 +2,68 @@
|
|
2
2
|
|
3
3
|
module Hubspot
|
4
4
|
class Contact < Resource
|
5
|
+
# def required_properties
|
6
|
+
# %w[email firstname lastname]
|
7
|
+
# end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def metadata_field?(key)
|
12
|
+
METADATA_FIELDS.include?(key) || key.start_with?('hs_')
|
13
|
+
end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
# Finds a contact by the hubspotutk cookie
|
17
|
+
#
|
18
|
+
# token - the hubspot tracking token (stored from the hubspotutk cookie value)
|
19
|
+
# properties: - Optional list of properties to return.
|
20
|
+
# Note: If properties are specified 2 calls to the api will be made because
|
21
|
+
# at this time you can only search by the token using the v1 api
|
22
|
+
# from which we
|
23
|
+
#
|
24
|
+
# Example:
|
25
|
+
# properties = %w[firstname lastname email last_contacted]
|
26
|
+
# contact = Hubspot::Contact.find_by_token(hubspotutk_cookie_value, properties)
|
27
|
+
#
|
28
|
+
# Returns An instance of the resource.
|
29
|
+
def find_by_token(token, properties: [])
|
30
|
+
all_properties = build_property_list(properties)
|
31
|
+
query_props = all_properties.map { |prop| "property=#{prop}" }
|
32
|
+
query_string = query_props.concat(['propertyMode=value_only']).join('&')
|
33
|
+
|
34
|
+
# Make the original API request, manually appending the query string
|
35
|
+
response = get("/contacts/v1/contact/utk/#{token}/profile?#{query_string}")
|
36
|
+
|
37
|
+
# Only modify the response if it's successful (status 200 OK)
|
38
|
+
if response.success?
|
39
|
+
# Convert the v1 response body (parsed_response) to a v3 structure
|
40
|
+
v3_response_hash = convert_v1_response(response.parsed_response, all_properties)
|
41
|
+
|
42
|
+
# Modify the existing response object by updating its `parsed_response`
|
43
|
+
response.instance_variable_set(:@parsed_response, v3_response_hash)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Pass the (potentially modified) HTTParty response to the next step
|
47
|
+
instantiate_from_response(response)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def convert_v1_response(v1_response, property_list)
|
53
|
+
# Extract the `vid` as `id`
|
54
|
+
v3_response = {
|
55
|
+
'id' => v1_response['vid']
|
56
|
+
}
|
57
|
+
|
58
|
+
properties = property_list.each_with_object({}) do |property, hash|
|
59
|
+
hash[property] = v1_response.dig('properties', property, 'value')
|
60
|
+
end
|
61
|
+
|
62
|
+
# Build the v3 structure
|
63
|
+
v3_response['properties'] = properties
|
64
|
+
|
65
|
+
v3_response
|
66
|
+
end
|
67
|
+
end
|
5
68
|
end
|
6
69
|
end
|
data/lib/hubspot/exceptions.rb
CHANGED
@@ -19,6 +19,7 @@ module Hubspot
|
|
19
19
|
class RateLimitExceededError < RequestError; end
|
20
20
|
class NotConfiguredError < StandardError; end
|
21
21
|
class ArgumentError < StandardError; end
|
22
|
+
class NothingToDoError < StandardError; end
|
22
23
|
|
23
24
|
class << self
|
24
25
|
def error_from_response(response)
|
data/lib/hubspot/form.rb
CHANGED
@@ -4,11 +4,13 @@ module Hubspot
|
|
4
4
|
class Form < Resource
|
5
5
|
METADATA_FIELDS = %w[createdAt updatedAt archived].freeze
|
6
6
|
|
7
|
+
# :nocov:
|
7
8
|
def inspect
|
8
9
|
"#<#{self.class.name} " \
|
9
10
|
"@name=#{name}, " \
|
10
11
|
"@fieldGroups=#{respond_to?('fieldGroups') ? fieldGroups.size : '-'}>"
|
11
12
|
end
|
13
|
+
# :nocov:
|
12
14
|
|
13
15
|
class << self
|
14
16
|
def api_root
|
@@ -18,9 +20,9 @@ module Hubspot
|
|
18
20
|
|
19
21
|
private
|
20
22
|
|
21
|
-
#
|
22
|
-
def extract_id(
|
23
|
-
|
23
|
+
# dont convert (from string)
|
24
|
+
def extract_id(id)
|
25
|
+
id
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
data/lib/hubspot/paged_batch.rb
CHANGED
@@ -10,7 +10,7 @@ module Hubspot
|
|
10
10
|
|
11
11
|
MAX_LIMIT = 100 # HubSpot max items per page
|
12
12
|
|
13
|
-
#
|
13
|
+
# :nocov:
|
14
14
|
def inspect
|
15
15
|
"#<#{self.class.name} " \
|
16
16
|
"@url=#{@url.inspect}, " \
|
@@ -18,6 +18,7 @@ module Hubspot
|
|
18
18
|
"@resource_class=#{@resource_class.inspect}, " \
|
19
19
|
"@object_ids_count=#{@object_ids.size}>"
|
20
20
|
end
|
21
|
+
# :nocov:
|
21
22
|
|
22
23
|
# rubocop:disable Lint/MissingSuper
|
23
24
|
def initialize(url:, params: {}, resource_class: nil, object_ids: [])
|
data/lib/hubspot/resource.rb
CHANGED
@@ -45,12 +45,14 @@ module Hubspot
|
|
45
45
|
# Find a resource by ID and return an instance of the class
|
46
46
|
#
|
47
47
|
# id - [Integer] The ID (or hs_object_id) of the resource to fetch.
|
48
|
+
# properties - an array of property names to fetch in the result
|
48
49
|
#
|
49
50
|
# Example:
|
50
51
|
# contact = Hubspot::Contact.find(1)
|
52
|
+
# contact = Hubspot::Contact.find(1, properties: %w[email firstname lastname custom_field])
|
51
53
|
#
|
52
54
|
# Returns An instance of the resource.
|
53
|
-
def find(id, properties
|
55
|
+
def find(id, properties: nil)
|
54
56
|
all_properties = build_property_list(properties)
|
55
57
|
if all_properties.is_a?(Array) && !all_properties.empty?
|
56
58
|
params = { query: { properties: all_properties } }
|
@@ -181,8 +183,8 @@ module Hubspot
|
|
181
183
|
# Hubspot::Contact.batch_read_all(hubspot_contact_ids)
|
182
184
|
#
|
183
185
|
# Returns [Hubspot::Batch] A batch of resources that can be operated on further
|
184
|
-
def batch_read_all(object_ids = [], id_property: 'id')
|
185
|
-
Hubspot::Batch.read(self, object_ids, id_property: id_property)
|
186
|
+
def batch_read_all(object_ids = [], properties: [], id_property: 'id')
|
187
|
+
Hubspot::Batch.read(self, object_ids, properties: properties, id_property: id_property)
|
186
188
|
end
|
187
189
|
|
188
190
|
# Retrieve the complete list of properties for this resource class
|
@@ -261,6 +263,8 @@ module Hubspot
|
|
261
263
|
#
|
262
264
|
# If no suffix is provided, the default comparison is equality (`EQ`).
|
263
265
|
#
|
266
|
+
# If no value is provided, or is empty the NOT_HAS_PROPERTY operator will be used
|
267
|
+
#
|
264
268
|
# query - [String, Hash] The query for searching. This can be either:
|
265
269
|
# - A String: for full-text search.
|
266
270
|
# - A Hash: where each key represents a property and may have suffixes for the comparison
|
@@ -354,9 +358,9 @@ module Hubspot
|
|
354
358
|
filter_groups = [{ filters: [] }]
|
355
359
|
|
356
360
|
filters.each do |key, value|
|
357
|
-
filter = extract_property_and_operator(key)
|
361
|
+
filter = extract_property_and_operator(key, value)
|
358
362
|
value_key = value.is_a?(Array) ? :values : :value
|
359
|
-
filter[value_key] = value
|
363
|
+
filter[value_key] = value unless value.blank?
|
360
364
|
filter_groups.first[:filters] << filter
|
361
365
|
end
|
362
366
|
|
@@ -364,7 +368,9 @@ module Hubspot
|
|
364
368
|
end
|
365
369
|
|
366
370
|
# Extract property name and operator from the key
|
367
|
-
def extract_property_and_operator(key)
|
371
|
+
def extract_property_and_operator(key, value)
|
372
|
+
return { propertyName: key.to_s, operator: 'NOT_HAS_PROPERTY' } if value.blank?
|
373
|
+
|
368
374
|
OPERATOR_MAP.each do |suffix, hubspot_operator|
|
369
375
|
if key.to_s.end_with?(suffix)
|
370
376
|
return {
|
@@ -392,8 +398,13 @@ module Hubspot
|
|
392
398
|
|
393
399
|
# Public: Initialize a resouce
|
394
400
|
#
|
395
|
-
# data - [2D Hash, nested Hash] data to initialise
|
396
|
-
# -
|
401
|
+
# data - [2D Hash, nested Hash] data to initialise:
|
402
|
+
# - The response from the api will be of the form:
|
403
|
+
# { id: <hs_object_id>, properties: { "email": "john@example.org" ... }, ... }
|
404
|
+
#
|
405
|
+
# - A Simple 2D Hash, key value pairs in the form:
|
406
|
+
# { email: 'john@example.org', firstname: 'John', lastname: 'Smith' }
|
407
|
+
#
|
397
408
|
# - A structured hash consisting of { id: <hs_object_id>, properties: {}, ... }
|
398
409
|
# This is the same structure as per the API, and can be rebuilt if you store the id
|
399
410
|
# of the object against your own data
|
@@ -409,7 +420,7 @@ module Hubspot
|
|
409
420
|
# existing_contact = Hubspot::Contact.new(id: hubspot_id, properties: contact.to_hubspot)
|
410
421
|
def initialize(data = {})
|
411
422
|
data.transform_keys!(&:to_s)
|
412
|
-
@id = extract_id(data)
|
423
|
+
@id = extract_id(data.delete('id'))
|
413
424
|
@properties = {}
|
414
425
|
@metadata = {}
|
415
426
|
if @id
|
@@ -447,6 +458,12 @@ module Hubspot
|
|
447
458
|
end
|
448
459
|
end
|
449
460
|
|
461
|
+
def save!
|
462
|
+
raise NothingToDoError, 'Nothing to save' unless changes?
|
463
|
+
|
464
|
+
save
|
465
|
+
end
|
466
|
+
|
450
467
|
# If the resource exists in Hubspot
|
451
468
|
#
|
452
469
|
# Returns Boolean
|
@@ -520,15 +537,8 @@ module Hubspot
|
|
520
537
|
if method_name.end_with?('=')
|
521
538
|
attribute = method_name.chomp('=')
|
522
539
|
new_value = args.first
|
523
|
-
|
524
|
-
#
|
525
|
-
if @properties[attribute] != new_value
|
526
|
-
@changes[attribute] = new_value
|
527
|
-
else
|
528
|
-
@changes.delete(attribute) # Remove from changes if it reverts to the original value
|
529
|
-
end
|
530
|
-
|
531
|
-
return new_value
|
540
|
+
add_accessors attribute
|
541
|
+
return send("#{attribute}=", new_value)
|
532
542
|
# Handle getters
|
533
543
|
else
|
534
544
|
return @changes[method_name] if @changes.key?(method_name)
|
@@ -547,35 +557,70 @@ module Hubspot
|
|
547
557
|
@properties.key?(property_name) || @changes.key?(property_name) || super
|
548
558
|
end
|
549
559
|
|
550
|
-
private
|
551
|
-
|
552
|
-
# Extract ID from data and convert to integer
|
553
|
-
def extract_id(data)
|
554
|
-
data['id'] ? data['id'].to_i : nil
|
555
|
-
end
|
556
|
-
|
557
560
|
# Initialize from API response, separating metadata from properties
|
558
561
|
def initialize_from_api(data)
|
562
|
+
@changes = data.delete('changes')&.transform_keys!(&:to_s) || {}
|
563
|
+
|
559
564
|
if data['properties']
|
560
565
|
@metadata = data.reject { |key, _v| key == 'properties' }
|
561
566
|
handle_properties(data['properties'])
|
562
567
|
else
|
563
568
|
handle_properties(data)
|
564
569
|
end
|
570
|
+
end
|
565
571
|
|
566
|
-
|
572
|
+
private
|
573
|
+
|
574
|
+
# Extract ID from data and convert to integer
|
575
|
+
def extract_id(id)
|
576
|
+
id&.to_i
|
567
577
|
end
|
568
578
|
|
569
579
|
def handle_properties(properties_data)
|
570
|
-
properties_data.each do |
|
571
|
-
if
|
572
|
-
@metadata[
|
580
|
+
properties_data.each do |attribute, value|
|
581
|
+
if metadata_field?(attribute)
|
582
|
+
@metadata[attribute.to_s] = value
|
583
|
+
else
|
584
|
+
add_accessors attribute.to_s
|
585
|
+
@properties[attribute.to_s] = value
|
586
|
+
end
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
def add_accessors(attribute)
|
591
|
+
add_accessors_setter(attribute)
|
592
|
+
add_accessors_getter(attribute)
|
593
|
+
end
|
594
|
+
|
595
|
+
def add_accessors_setter(attribute)
|
596
|
+
# Define the setter method
|
597
|
+
define_singleton_method("#{attribute}=") do |new_value|
|
598
|
+
# Track changes only if the value has actually changed
|
599
|
+
if @properties[attribute] != new_value
|
600
|
+
@changes[attribute] = new_value
|
573
601
|
else
|
574
|
-
@
|
602
|
+
@changes.delete(attribute) # Remove from changes if it reverts to the original value
|
575
603
|
end
|
604
|
+
|
605
|
+
new_value
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
def add_accessors_getter(attribute)
|
610
|
+
# Define the getter method
|
611
|
+
define_singleton_method(attribute) do
|
612
|
+
# Return from changes if available, else return from properties
|
613
|
+
return @changes[attribute] if @changes.key?(attribute)
|
614
|
+
|
615
|
+
@properties[attribute] if @properties.key?(attribute)
|
576
616
|
end
|
577
617
|
end
|
578
618
|
|
619
|
+
# allows overwriting in other resource classes
|
620
|
+
def metadata_field?(key)
|
621
|
+
METADATA_FIELDS.include?(key)
|
622
|
+
end
|
623
|
+
|
579
624
|
# Initialize a new object (no API response)
|
580
625
|
def initialize_new_object(data)
|
581
626
|
@properties = {}
|
@@ -583,15 +628,12 @@ module Hubspot
|
|
583
628
|
@metadata = {}
|
584
629
|
end
|
585
630
|
|
586
|
-
# Extract metadata from data, excluding properties
|
587
|
-
def extract_metadata(data)
|
588
|
-
data.reject { |key, _| key == 'properties' }
|
589
|
-
end
|
590
|
-
|
591
631
|
# Create a new resource
|
592
632
|
def create_new
|
593
633
|
created_resource = self.class.create(@changes)
|
594
634
|
@id = created_resource.id
|
635
|
+
@properties.merge!(@changes)
|
636
|
+
@changes = {}
|
595
637
|
@id ? true : false
|
596
638
|
end
|
597
639
|
end
|
data/lib/hubspot/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_hubspot_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simon Brook
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-10-
|
11
|
+
date: 2024-10-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -254,7 +254,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
254
254
|
- !ruby/object:Gem::Version
|
255
255
|
version: '0'
|
256
256
|
requirements: []
|
257
|
-
|
257
|
+
rubyforge_project:
|
258
|
+
rubygems_version: 2.6.14
|
258
259
|
signing_key:
|
259
260
|
specification_version: 4
|
260
261
|
summary: ruby_hubspot_api is an ORM-like wrapper for the Hubspot API
|