diff --git a/History.md b/History.md index 2014dde6..cb79324c 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,7 @@ +## 0.18.0 + +* BREAKING CHANGE : Ticket#create! and Task#create now takes association param (array), build it using Association.build_association_param + ## 0.17.1 * Add default properties for Ticket#find diff --git a/hubspot-api-ruby.gemspec b/hubspot-api-ruby.gemspec index a6cc8b6f..ce14bf37 100644 --- a/hubspot-api-ruby.gemspec +++ b/hubspot-api-ruby.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = "hubspot-api-ruby" - s.version = "0.17.1" + s.version = "0.18.0" s.require_paths = ["lib"] s.authors = ["Jonathan"] s.email = ["jonathan@hoggo.com"] diff --git a/lib/hubspot/association.rb b/lib/hubspot/association.rb index ccf11e76..a7729473 100644 --- a/lib/hubspot/association.rb +++ b/lib/hubspot/association.rb @@ -75,6 +75,15 @@ def all(object_type, object_id, to_object_type) response['results'].map { |result| klass.find(result["toObjectId"]) } end + # Utility function to build the association list required by some API endpoints + def build_association_param(origin, associated, associated_id) + { + to: { id: associated_id }, + types: [{ associationCategory: 'HUBSPOT_DEFINED', + associationTypeId: Hubspot::Association::ASSOCIATION_DEFINITIONS[origin][associated] }] + } + end + private def build_create_association_body(association, definition_id) diff --git a/lib/hubspot/task.rb b/lib/hubspot/task.rb index d1dc5558..d297e9bd 100644 --- a/lib/hubspot/task.rb +++ b/lib/hubspot/task.rb @@ -20,17 +20,10 @@ def initialize(response_hash) end class << self - def create!(params = {}, ticket_id: nil) - associations_hash = { 'associations' => [] } - if ticket_id.present? - associations_hash['associations'] << { - "to": { "id": ticket_id }, - "types": [{ "associationCategory": 'HUBSPOT_DEFINED', - "associationTypeId": Hubspot::Association::ASSOCIATION_DEFINITIONS['Task']['Ticket'] }] - } - end + def create!(params = {}, associations: []) + associations_hash = { associations: } properties = { hs_task_status: 'NOT_STARTED', hs_task_type: 'TODO' }.merge(params) - post_data = associations_hash.merge({ properties: properties }) + post_data = associations_hash.merge(properties:) response = Hubspot::Connection.post_json(TASKS_PATH, params: {}, body: post_data) new(response) diff --git a/lib/hubspot/ticket.rb b/lib/hubspot/ticket.rb index b7b27f25..06fe7f02 100644 --- a/lib/hubspot/ticket.rb +++ b/lib/hubspot/ticket.rb @@ -20,31 +20,9 @@ def initialize(response_hash) end class << self - def create!(params = {}, contact_id: nil, company_id: nil, deal_id: nil) - associations_hash = { 'associations' => [] } - if contact_id.present? - associations_hash['associations'] << { - "to": { "id": contact_id }, - "types": [{ "associationCategory": 'HUBSPOT_DEFINED', - "associationTypeId": Hubspot::Association::ASSOCIATION_DEFINITIONS['Ticket']['Contact'] }] - } - end - if company_id.present? - associations_hash['associations'] << { - "to": { "id": company_id }, - "types": [{ "associationCategory": 'HUBSPOT_DEFINED', - "associationTypeId": Hubspot::Association::ASSOCIATION_DEFINITIONS['Ticket']['Company'] }] - } - end - if deal_id.present? - associations_hash['associations'] << { - "to": { "id": deal_id }, - "types": [{ "associationCategory": 'HUBSPOT_DEFINED', - "associationTypeId": Hubspot::Association::ASSOCIATION_DEFINITIONS['Ticket']['Deal'] }] - } - end + def create!(params = {}, associations: []) + associations_hash = { associations: } post_data = associations_hash.merge({ properties: params }) - response = Hubspot::Connection.post_json(TICKETS_PATH, params: {}, body: post_data) new(response) end diff --git a/spec/fixtures/vcr_cassettes/task.yml b/spec/fixtures/vcr_cassettes/task.yml index ceffaff4..ab792556 100644 --- a/spec/fixtures/vcr_cassettes/task.yml +++ b/spec/fixtures/vcr_cassettes/task.yml @@ -5,8 +5,8 @@ http_interactions: uri: https://api.hubapi.com/crm/v3/objects/tasks body: encoding: UTF-8 - string: '{"associations":[{"to":{"id":16174569112},"types":[{"associationCategory":"HUBSPOT_DEFINED","associationTypeId":230}]}],"properties":{"hs_task_status":"NOT_STARTED","hs_task_type":"TODO","hs_task_body":"i - am a task","hs_task_subject":"title of task","hs_timestamp":"1730980291412"}}' + string: '{"associations":[{"to":{"id":16174569112},"types":[{"associationCategory":"HUBSPOT_DEFINED","associationTypeId":230}]},{"to":{"id":75761595194},"types":[{"associationCategory":"HUBSPOT_DEFINED","associationTypeId":204}]},{"to":{"id":25571271600},"types":[{"associationCategory":"HUBSPOT_DEFINED","associationTypeId":192}]},{"to":{"id":28806796888},"types":[{"associationCategory":"HUBSPOT_DEFINED","associationTypeId":216}]}],"properties":{"hs_task_status":"NOT_STARTED","hs_task_type":"TODO","hs_task_body":"i + am a task","hs_task_subject":"title of task","hs_timestamp":"1732111731855"}}' headers: Authorization: - Bearer @@ -24,7 +24,7 @@ http_interactions: message: Created headers: Date: - - Thu, 07 Nov 2024 11:51:32 GMT + - Wed, 20 Nov 2024 14:08:52 GMT Content-Type: - application/json;charset=utf-8 Content-Length: @@ -32,9 +32,9 @@ http_interactions: Connection: - keep-alive Location: - - https://api.hubapi.com/crm/v3/objects/tasks/64075014222 + - https://api.hubapi.com/crm/v3/objects/tasks/65039244375 Cf-Ray: - - 8ded1ca5e8d0d70a-CDG + - 8e5903b4aaa16f0e-CDG Cf-Cache-Status: - DYNAMIC Strict-Transport-Security: @@ -46,11 +46,11 @@ http_interactions: X-Content-Type-Options: - nosniff X-Hubspot-Correlation-Id: - - f6e0983f-f1f2-4dbb-9a03-28499f0e87df + - ed5e936f-5d2d-4ad2-8beb-72f79c0ff48f X-Hubspot-Ratelimit-Daily: - '1000000' X-Hubspot-Ratelimit-Daily-Remaining: - - '999894' + - '999999' X-Hubspot-Ratelimit-Interval-Milliseconds: - '10000' X-Hubspot-Ratelimit-Max: @@ -62,16 +62,16 @@ http_interactions: X-Hubspot-Ratelimit-Secondly-Remaining: - '18' Report-To: - - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=oxvVtGSHCCKhoRjRe14twDypKCjwgEIh6e22X%2FLmtWKVgsRRb27POoL5Gd7j1NcyqK%2BqRLzZ3JeUjhuS0gKc7css6LgrE5Tu7aOtG%2BW0iNszBavmdUBMNVJENnwtUAsXStB0Ql5UwoIdRPnu"}],"group":"cf-nel","max_age":604800}' + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=fHqKZ2b5WxUO52SyuP8pubAeqYZmEENONadmd36c7UFlW20eBEEZcXZK%2F301Y9tpUJrgXlYhsYGZfh%2BCqvlZ%2BCiV5WTT53KcBK2KG%2Bs7R5hf87s997XYhuJfJLMUUZJbkIgc9mpYmFsSCbzE"}],"group":"cf-nel","max_age":604800}' Nel: - '{"success_fraction":0.01,"report_to":"cf-nel","max_age":604800}' Server: - cloudflare body: encoding: ASCII-8BIT - string: '{"id":"64075014222","properties":{"hs_body_preview":"i am a task","hs_body_preview_html":"\n - \n \n i am a task\n \n","hs_body_preview_is_truncated":"false","hs_createdate":"2024-11-07T11:51:31.747Z","hs_lastmodifieddate":"2024-11-07T11:51:31.747Z","hs_object_id":"64075014222","hs_object_source":"INTEGRATION","hs_object_source_id":"1024738","hs_object_source_label":"INTEGRATION","hs_task_body":"i + string: '{"id":"65039244375","properties":{"hs_body_preview":"i am a task","hs_body_preview_html":"\n + \n \n i am a task\n \n","hs_body_preview_is_truncated":"false","hs_createdate":"2024-11-20T14:08:52.191Z","hs_lastmodifieddate":"2024-11-20T14:08:52.191Z","hs_object_id":"65039244375","hs_object_source":"INTEGRATION","hs_object_source_id":"1024738","hs_object_source_label":"INTEGRATION","hs_task_body":"i am a task","hs_task_completion_count":"0","hs_task_family":"SALES","hs_task_for_object_type":"OWNER","hs_task_is_all_day":"false","hs_task_is_completed":"0","hs_task_is_completed_call":"0","hs_task_is_completed_email":"0","hs_task_is_completed_linked_in":"0","hs_task_is_completed_sequence":"0","hs_task_is_overdue":"true","hs_task_is_past_due_date":"true","hs_task_missed_due_date":"true","hs_task_missed_due_date_count":"1","hs_task_priority":"NONE","hs_task_status":"NOT_STARTED","hs_task_subject":"title - of task","hs_task_type":"TODO","hs_timestamp":"2024-11-07T11:51:31.412Z"},"createdAt":"2024-11-07T11:51:31.747Z","updatedAt":"2024-11-07T11:51:31.747Z","archived":false}' - recorded_at: Thu, 07 Nov 2024 11:51:32 GMT + of task","hs_task_type":"TODO","hs_timestamp":"2024-11-20T14:08:51.855Z"},"createdAt":"2024-11-20T14:08:52.191Z","updatedAt":"2024-11-20T14:08:52.191Z","archived":false}' + recorded_at: Wed, 20 Nov 2024 14:08:52 GMT recorded_with: VCR 6.3.1 diff --git a/spec/lib/hubspot/association_spec.rb b/spec/lib/hubspot/association_spec.rb index 905eac82..84185ced 100644 --- a/spec/lib/hubspot/association_spec.rb +++ b/spec/lib/hubspot/association_spec.rb @@ -162,4 +162,17 @@ end end end + + describe '.build_association_param' do + it 'returns proper association hash' do + result = described_class.build_association_param('Ticket', 'Company', 1337) + expect(result).to eq( + { + "to": { "id": 1337 }, + "types": [{ "associationCategory": 'HUBSPOT_DEFINED', + "associationTypeId": 339 }] + } + ) + end + end end diff --git a/spec/lib/hubspot/task_spec.rb b/spec/lib/hubspot/task_spec.rb index 8ca29f46..343cef8d 100644 --- a/spec/lib/hubspot/task_spec.rb +++ b/spec/lib/hubspot/task_spec.rb @@ -6,7 +6,13 @@ hs_task_subject: 'title of task', hs_timestamp: DateTime.now.strftime('%Q') } - described_class.create!(params, ticket_id: 16_174_569_112) + associations = [ + Hubspot::Association.build_association_param('Task', 'Ticket', 16_174_569_112), + Hubspot::Association.build_association_param('Task', 'Contact', 75_761_595_194), + Hubspot::Association.build_association_param('Task', 'Company', 25_571_271_600), + Hubspot::Association.build_association_param('Task', 'Deal', 28_806_796_888), + ] + described_class.create!(params, associations:) end it 'creates a new task with valid properties' do diff --git a/spec/lib/hubspot/ticket_spec.rb b/spec/lib/hubspot/ticket_spec.rb index 56c1f0a3..fcee0fc6 100644 --- a/spec/lib/hubspot/ticket_spec.rb +++ b/spec/lib/hubspot/ticket_spec.rb @@ -7,7 +7,12 @@ hs_ticket_priority: 'MEDIUM', subject: 'test ticket' } - described_class.create!(params, contact_id: 75_761_595_194, company_id: 25_571_271_600, deal_id: 28_806_796_888) + associations = [ + Hubspot::Association.build_association_param('Ticket', 'Contact', 75_761_595_194), + Hubspot::Association.build_association_param('Ticket', 'Company', 25_571_271_600), + Hubspot::Association.build_association_param('Ticket', 'Deal', 28_806_796_888), + ] + described_class.create!(params, associations:) end it 'creates a new ticket with valid properties' do