From c9f4da335b80fc8e6af397a29896383490b60f89 Mon Sep 17 00:00:00 2001 From: AmrIDG Date: Sun, 16 Nov 2025 15:30:45 +0200 Subject: [PATCH 1/7] Create private_client.ex --- lib/hubspot/manage/private_client.ex | 108 +++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 lib/hubspot/manage/private_client.ex diff --git a/lib/hubspot/manage/private_client.ex b/lib/hubspot/manage/private_client.ex new file mode 100644 index 0000000..7dff836 --- /dev/null +++ b/lib/hubspot/manage/private_client.ex @@ -0,0 +1,108 @@ +defmodule Hubspot.Manage.PrivateClient do + alias Hubspot.Common.API + + def request(access_token, post_type, object_type, object, opts \\ []) + + def request(access_token, post_type, object_type, %{inputs: _objects} = payload, opts) + when object_type in [:user, :organization, :application] do + case API.request( + :post, + "crm/v3/objects/#{to_object_type(object_type)}/batch/#{post_type}", + Jason.encode!(payload), + [ + {"Content-type", "application/json"}, + {"authorization", "Bearer #{access_token}"}, + {"accept", "application/json"} + ] + ) do + {:ok, %{status: status, body: body}} -> {:ok, body} + {:not_found, reason} -> {:error, reason} + error -> error + end + end + + def request(access_token, method, :association, %{inputs: _objects} = payload, opts) do + case API.request( + :post, + "/crm/v3/associations/#{to_object_type(opts[:from_type])}/#{to_object_type(opts[:to_type])}/batch/#{method}", + Jason.encode!(payload), + [ + {"authorization", "Bearer #{access_token}"}, + {"accept", "application/json"}, + {"Content-Type", "application/json"} + ] + ) do + {:ok, %{status: status, body: body}} -> {:ok, body} + {:error, %{status: 404}} -> {:error, :not_found} + error -> error + end + end + + def request(access_token, :get, object_type, object, opts) + when object_type in [:user, :organization, :application] do + case API.request( + :get, + "crm/v3/objects/#{to_object_type(object_type)}/#{object.id}?idProperty=#{id_property(object_type)}", + nil, + [ + {"authorization", "Bearer #{access_token}"}, + {"accept", "application/json"}, + {"Content-Type", "application/json"} + ] + ) do + {:ok, %{status: status, body: body}} -> {:ok, body} + {:error, %{status: 404}} -> {:error, :not_found} + error -> error + end + end + + def request(access_token, :create, object_type, object, opts) + when object_type in [:user, :organization, :application] do + case API.request( + :post, + "crm/v3/objects/#{to_object_type(object_type)}", + Jason.encode!(object), + [ + {"authorization", "Bearer #{access_token}"}, + {"accept", "application/json"}, + {"Content-Type", "application/json"} + ] + ) do + {:ok, %{status: status, body: body}} -> {:ok, body} + {:error, %{status: 404}} -> {:error, :not_found} + error -> error + end + end + + def request(access_token, :update, object_type, object, opts) + when object_type in [:user, :organization, :application] do + case API.request( + :patch, + "crm/v3/objects/#{to_object_type(object_type)}/#{object.id}?idProperty=#{id_property(object_type)}", + Jason.encode!(object), + [ + {"authorization", "Bearer #{access_token}"}, + {"accept", "application/json"}, + {"Content-Type", "application/json"} + ] + ) do + {:ok, %{status: status, body: body}} -> {:ok, body} + {:error, %{status: 404}} -> {:error, :not_found} + error -> error + end + end + + def request(_client_code, _access_token, object_type, _objects, _opts), + do: {:error, "unsupported object_type #{inspect(object_type)}"} + + # ------------------------------------------------------------ + # Object Type Mapping + # ------------------------------------------------------------ + defp to_object_type(type) when is_binary(type), do: type + defp to_object_type(:organization), do: "p147145418_organizations" + defp to_object_type(:application), do: "p147145418_applications" + defp to_object_type(:user), do: "p147145418_users" + defp id_property(:organization), do: "id" + defp id_property(:application), do: "application_id" + defp id_property(:user), do: "id" +end From 6e3ed53167e0d15c26768306ebcde85c83c0583c Mon Sep 17 00:00:00 2001 From: AmrIDG Date: Sun, 16 Nov 2025 15:38:02 +0200 Subject: [PATCH 2/7] Update private_client.ex --- lib/hubspot/manage/private_client.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/hubspot/manage/private_client.ex b/lib/hubspot/manage/private_client.ex index 7dff836..e9c6c5f 100644 --- a/lib/hubspot/manage/private_client.ex +++ b/lib/hubspot/manage/private_client.ex @@ -16,7 +16,7 @@ defmodule Hubspot.Manage.PrivateClient do ] ) do {:ok, %{status: status, body: body}} -> {:ok, body} - {:not_found, reason} -> {:error, reason} + {:error, %{status: 404}} -> {:error, :not_found} error -> error end end From d2bff6c3fa44adb4eb1c33d2c61955a5a480bacc Mon Sep 17 00:00:00 2001 From: AmrIDG Date: Sun, 23 Nov 2025 12:18:07 +0200 Subject: [PATCH 3/7] Update private_client.ex --- lib/hubspot/manage/private_client.ex | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/hubspot/manage/private_client.ex b/lib/hubspot/manage/private_client.ex index e9c6c5f..60bae64 100644 --- a/lib/hubspot/manage/private_client.ex +++ b/lib/hubspot/manage/private_client.ex @@ -1,5 +1,6 @@ defmodule Hubspot.Manage.PrivateClient do alias Hubspot.Common.API + @object_prefix "p4160154_" def request(access_token, post_type, object_type, object, opts \\ []) @@ -98,11 +99,8 @@ defmodule Hubspot.Manage.PrivateClient do # ------------------------------------------------------------ # Object Type Mapping # ------------------------------------------------------------ - defp to_object_type(type) when is_binary(type), do: type - defp to_object_type(:organization), do: "p147145418_organizations" - defp to_object_type(:application), do: "p147145418_applications" - defp to_object_type(:user), do: "p147145418_users" + defp to_object_type(type) , do: "#{@object_prefix}#{to_string(type)}s" defp id_property(:organization), do: "id" defp id_property(:application), do: "application_id" - defp id_property(:user), do: "id" + defp id_property(:user), do: "email" end From 7d19805c65eaf02bdb61f00c08d46bedea89d01e Mon Sep 17 00:00:00 2001 From: AmrIDG Date: Mon, 24 Nov 2025 09:16:42 +0200 Subject: [PATCH 4/7] Update private_client.ex --- lib/hubspot/manage/private_client.ex | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/hubspot/manage/private_client.ex b/lib/hubspot/manage/private_client.ex index 60bae64..af521ab 100644 --- a/lib/hubspot/manage/private_client.ex +++ b/lib/hubspot/manage/private_client.ex @@ -1,6 +1,6 @@ defmodule Hubspot.Manage.PrivateClient do alias Hubspot.Common.API - @object_prefix "p4160154_" + @object_prefix "p4160154" def request(access_token, post_type, object_type, object, opts \\ []) @@ -99,7 +99,10 @@ defmodule Hubspot.Manage.PrivateClient do # ------------------------------------------------------------ # Object Type Mapping # ------------------------------------------------------------ - defp to_object_type(type) , do: "#{@object_prefix}#{to_string(type)}s" + defp to_object_type(:user), do: "#{@object_prefix}_users" + defp to_object_type(:organization), do: "#{@object_prefix}_organizations" + defp to_object_type(:application), do: "#{@object_prefix}_applications" + defp id_property(:organization), do: "id" defp id_property(:application), do: "application_id" defp id_property(:user), do: "email" From a206a2e17cc96cfd30f0874499adb25fcd6bf521 Mon Sep 17 00:00:00 2001 From: AmrIDG Date: Mon, 1 Dec 2025 10:48:38 +0200 Subject: [PATCH 5/7] Update private_client.ex --- lib/hubspot/manage/private_client.ex | 130 +++++++++++---------------- 1 file changed, 52 insertions(+), 78 deletions(-) diff --git a/lib/hubspot/manage/private_client.ex b/lib/hubspot/manage/private_client.ex index af521ab..6ad1a40 100644 --- a/lib/hubspot/manage/private_client.ex +++ b/lib/hubspot/manage/private_client.ex @@ -1,107 +1,81 @@ defmodule Hubspot.Manage.PrivateClient do alias Hubspot.Common.API - @object_prefix "p4160154" + + @valid_types [:user, :organization, :application] + @json_headers [ + {"content-type", "application/json"}, + {"accept", "application/json"} + ] def request(access_token, post_type, object_type, object, opts \\ []) def request(access_token, post_type, object_type, %{inputs: _objects} = payload, opts) - when object_type in [:user, :organization, :application] do - case API.request( - :post, - "crm/v3/objects/#{to_object_type(object_type)}/batch/#{post_type}", - Jason.encode!(payload), - [ - {"Content-type", "application/json"}, - {"authorization", "Bearer #{access_token}"}, - {"accept", "application/json"} - ] - ) do - {:ok, %{status: status, body: body}} -> {:ok, body} - {:error, %{status: 404}} -> {:error, :not_found} - error -> error - end + when object_type in @valid_types do + custom_object = custom_object(object_type, opts[:object_prefix]) + headers = [{"authorization", "Bearer " <> access_token} | @json_headers] + body = Jason.encode!(payload) + url = "crm/v3/objects/#{custom_object}/batch/#{post_type}" + + send_request(:post, url, body, headers) end def request(access_token, method, :association, %{inputs: _objects} = payload, opts) do - case API.request( - :post, - "/crm/v3/associations/#{to_object_type(opts[:from_type])}/#{to_object_type(opts[:to_type])}/batch/#{method}", - Jason.encode!(payload), - [ - {"authorization", "Bearer #{access_token}"}, - {"accept", "application/json"}, - {"Content-Type", "application/json"} - ] - ) do - {:ok, %{status: status, body: body}} -> {:ok, body} - {:error, %{status: 404}} -> {:error, :not_found} - error -> error - end + from_object = custom_object(opts[:from_type], opts[:object_prefix]) + to_object = custom_object(opts[:to_type], opts[:object_prefix]) + url = "/crm/v3/associations/#{from_object}/#{to_object}/batch/#{method}" + headers = [{"authorization", "Bearer " <> access_token} | @json_headers] + body = Jason.encode!(payload) + + send_request(:post, url, body, headers) end def request(access_token, :get, object_type, object, opts) - when object_type in [:user, :organization, :application] do - case API.request( - :get, - "crm/v3/objects/#{to_object_type(object_type)}/#{object.id}?idProperty=#{id_property(object_type)}", - nil, - [ - {"authorization", "Bearer #{access_token}"}, - {"accept", "application/json"}, - {"Content-Type", "application/json"} - ] - ) do - {:ok, %{status: status, body: body}} -> {:ok, body} - {:error, %{status: 404}} -> {:error, :not_found} - error -> error - end + when object_type in @valid_types do + custom_object = custom_object(object_type, opts[:object_prefix]) + headers = [{"authorization", "Bearer " <> access_token} | @json_headers] + url = "crm/v3/objects/#{custom_object}/#{object.id}?idProperty=#{id_property(object_type)}" + + send_request(:get, url, nil, headers) end def request(access_token, :create, object_type, object, opts) - when object_type in [:user, :organization, :application] do - case API.request( - :post, - "crm/v3/objects/#{to_object_type(object_type)}", - Jason.encode!(object), - [ - {"authorization", "Bearer #{access_token}"}, - {"accept", "application/json"}, - {"Content-Type", "application/json"} - ] - ) do - {:ok, %{status: status, body: body}} -> {:ok, body} - {:error, %{status: 404}} -> {:error, :not_found} - error -> error - end + when object_type in @valid_types do + custom_object = custom_object(object_type, opts[:object_prefix]) + headers = [{"authorization", "Bearer " <> access_token} | @json_headers] + url = "crm/v3/objects/#{custom_object}" + body = Jason.encode!(object) + + send_request(:post, url, body, headers) end def request(access_token, :update, object_type, object, opts) - when object_type in [:user, :organization, :application] do - case API.request( - :patch, - "crm/v3/objects/#{to_object_type(object_type)}/#{object.id}?idProperty=#{id_property(object_type)}", - Jason.encode!(object), - [ - {"authorization", "Bearer #{access_token}"}, - {"accept", "application/json"}, - {"Content-Type", "application/json"} - ] - ) do - {:ok, %{status: status, body: body}} -> {:ok, body} - {:error, %{status: 404}} -> {:error, :not_found} - error -> error - end + when object_type in @valid_types do + custom_object = custom_object(object_type, opts[:object_prefix]) + headers = [{"authorization", "Bearer " <> access_token} | @json_headers] + url = "crm/v3/objects/#{custom_object}/#{object.id}?idProperty=#{id_property(object_type)}" + body = Jason.encode!(object) + + send_request(:patch, url, body, headers) end def request(_client_code, _access_token, object_type, _objects, _opts), do: {:error, "unsupported object_type #{inspect(object_type)}"} + defp send_request(method, url, body, headers) do + case API.request(method, url, body, headers) do + {:ok, %{status: _status, body: body}} -> {:ok, body} + {:error, %{status: 404}} -> {:error, :not_found} + error -> error + end + end + # ------------------------------------------------------------ # Object Type Mapping # ------------------------------------------------------------ - defp to_object_type(:user), do: "#{@object_prefix}_users" - defp to_object_type(:organization), do: "#{@object_prefix}_organizations" - defp to_object_type(:application), do: "#{@object_prefix}_applications" + + defp custom_object(:user, object_prefix), do: "#{object_prefix}_users" + defp custom_object(:organization, object_prefix), do: "#{object_prefix}_organizations" + defp custom_object(:application, object_prefix), do: "#{object_prefix}_applications" defp id_property(:organization), do: "id" defp id_property(:application), do: "application_id" From 0cf16f2e6763a34be0f461b3b9984bf33c48d722 Mon Sep 17 00:00:00 2001 From: AmrIDG Date: Thu, 4 Dec 2025 11:10:47 +0200 Subject: [PATCH 6/7] add individual association request --- lib/hubspot/common/api.ex | 3 +++ lib/hubspot/manage/private_client.ex | 28 ++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/lib/hubspot/common/api.ex b/lib/hubspot/common/api.ex index d903949..d28ddeb 100644 --- a/lib/hubspot/common/api.ex +++ b/lib/hubspot/common/api.ex @@ -74,6 +74,9 @@ defmodule Hubspot.Common.API do when status == 404, do: {:error, %{status: status, body: body, headers: headers}} + defp decode_response(%Finch.Response{status: status, body: "", headers: headers} = _response), + do: {:error, %{status: status, body: "", headers: headers}} + defp decode_response(%Finch.Response{status: status, body: body, headers: headers} = _response), do: {:error, %{status: status, body: Jason.decode!(body), headers: headers}} end diff --git a/lib/hubspot/manage/private_client.ex b/lib/hubspot/manage/private_client.ex index 6ad1a40..e37c91f 100644 --- a/lib/hubspot/manage/private_client.ex +++ b/lib/hubspot/manage/private_client.ex @@ -29,11 +29,29 @@ defmodule Hubspot.Manage.PrivateClient do send_request(:post, url, body, headers) end - def request(access_token, :get, object_type, object, opts) + def request( + access_token, + :create, + :association, + %{from_id: from_id, to_id: to_id, association_type: association_type}, + opts + ) do + from_object = custom_object(opts[:from_type], opts[:object_prefix]) + to_object = custom_object(opts[:to_type], opts[:object_prefix]) + + url = + "/crm/v3/objects/#{from_object}/#{from_id}/associations/#{to_object}/#{to_id}/#{association_type}" + + headers = [{"authorization", "Bearer " <> access_token} | @json_headers] + + send_request(:post, url, "", headers) + end + + def request(access_token, :get, object_type, %{id: id}, opts) when object_type in @valid_types do custom_object = custom_object(object_type, opts[:object_prefix]) headers = [{"authorization", "Bearer " <> access_token} | @json_headers] - url = "crm/v3/objects/#{custom_object}/#{object.id}?idProperty=#{id_property(object_type)}" + url = "crm/v3/objects/#{custom_object}/#{id}?idProperty=#{id_property(object_type)}" send_request(:get, url, nil, headers) end @@ -58,8 +76,10 @@ defmodule Hubspot.Manage.PrivateClient do send_request(:patch, url, body, headers) end - def request(_client_code, _access_token, object_type, _objects, _opts), - do: {:error, "unsupported object_type #{inspect(object_type)}"} + def request(_access_token, method, object_type, _object, _opts), + do: + {:error, + "unsupported request method #{inspect(method)}, object_type #{inspect(object_type)}"} defp send_request(method, url, body, headers) do case API.request(method, url, body, headers) do From f6de6b363bf9b16709e06ccbdae5a7ed30cd9afe Mon Sep 17 00:00:00 2001 From: AmrIDG Date: Thu, 4 Dec 2025 11:54:25 +0200 Subject: [PATCH 7/7] fix association creation request type --- lib/hubspot/manage/private_client.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/hubspot/manage/private_client.ex b/lib/hubspot/manage/private_client.ex index e37c91f..fd10d14 100644 --- a/lib/hubspot/manage/private_client.ex +++ b/lib/hubspot/manage/private_client.ex @@ -44,7 +44,7 @@ defmodule Hubspot.Manage.PrivateClient do headers = [{"authorization", "Bearer " <> access_token} | @json_headers] - send_request(:post, url, "", headers) + send_request(:put, url, "", headers) end def request(access_token, :get, object_type, %{id: id}, opts)