From 176691cdc3239d5d158765c0f639a99e8f44144c Mon Sep 17 00:00:00 2001 From: Waseem Awashra Date: Tue, 2 Sep 2025 12:35:20 +0300 Subject: [PATCH 1/5] Adapt a new API for Standard/Custom objects --- lib/api.ex | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/lib/api.ex b/lib/api.ex index 01b90eb..cba0a43 100644 --- a/lib/api.ex +++ b/lib/api.ex @@ -1,6 +1,22 @@ defmodule ExForce.API do require Logger + @standard_objects [ + "Account", + "Campaign", + "Case", + "Contact", + "Contract", + "Lead", + "Opportunity", + "Product", + "Pricebook", + "Quotev", + "Solution", + "Task", + "User" + ] + @moduledoc """ Simple wrapper for EXForce library for userpilot needs. """ @@ -66,6 +82,35 @@ defmodule ExForce.API do SalesforceKB.refresh_app_token(config) end + @spec get_available_objects(binary()) :: {:ok, list()} | {:error, any()} + def get_available_objects(app_token) do + with {:ok, client} <- get_client(app_token), + {:ok, %{"sobjects" => objects}} <- ExForce.describe_global(client) do + objects + |> Enum.filter(&targeted_object?/1) + |> Enum.reject(&untargeted_object?/1) + |> Enum.map(fn object -> Map.delete(object, "urls") end) + end + end + + @spec get_available_custom_objects(binary()) :: {:ok, list()} | {:error, any()} + def get_available_custom_objects(app_token) do + with {:ok, client} <- get_client(app_token) do + {:ok, %{"sobjects" => objects}} = ExForce.describe_global(client) + + objects + |> Enum.filter(fn object -> object["custom"] == true end) + |> Enum.reject(fn object -> String.contains?(object["name"], "Userpilot") end) + |> Enum.map(fn object -> + %{ + name: object["name"], + label: object["label"], + custom: object["custom"] + } + end) + end + end + @doc """ Example: @@ -120,8 +165,7 @@ defmodule ExForce.API do number(), number() ) :: list() - def get_objects_paginated(app_token, object, param_list, per_page, page) - when object in ["Contact", "Lead", "Account"] do + def get_objects_paginated(app_token, object, param_list, per_page, page) do with {:ok, client} <- get_client(app_token) do param_list = maybe_append_id(param_list) @@ -529,4 +573,12 @@ defmodule ExForce.API do defp maybe_add_last_modified(query, last_seen), do: query <> " AND LastModifiedDate >= #{last_seen}" + + defp targeted_object?(object), + do: + object["name"] in @standard_objects or + String.ends_with?(object["name"], "__c") + + defp untargeted_object?(object), + do: String.contains?(object["name"], "Userpilot") end From ce7b4820cab3086c2ef3e2e7a8b97e68ada21b55 Mon Sep 17 00:00:00 2001 From: Waseem Awashra Date: Tue, 2 Sep 2025 19:18:50 +0300 Subject: [PATCH 2/5] unify the responses --- lib/api.ex | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/lib/api.ex b/lib/api.ex index cba0a43..cef0f53 100644 --- a/lib/api.ex +++ b/lib/api.ex @@ -89,7 +89,10 @@ defmodule ExForce.API do objects |> Enum.filter(&targeted_object?/1) |> Enum.reject(&untargeted_object?/1) - |> Enum.map(fn object -> Map.delete(object, "urls") end) + |> Enum.map(&to_object(&1, :standard_object)) + else + {:error, error} -> + {:error, error} end end @@ -101,13 +104,10 @@ defmodule ExForce.API do objects |> Enum.filter(fn object -> object["custom"] == true end) |> Enum.reject(fn object -> String.contains?(object["name"], "Userpilot") end) - |> Enum.map(fn object -> - %{ - name: object["name"], - label: object["label"], - custom: object["custom"] - } - end) + |> Enum.map(&to_object(&1, :custom_object)) + else + {:error, error} -> + {:error, error} end end @@ -143,9 +143,7 @@ defmodule ExForce.API do with {:ok, client} <- get_client(app_token), {:ok, %{"fields" => fields}} <- ExForce.describe_sobject(client, object) do fields - |> Enum.map(fn field -> - %{title: field["label"], id: field["name"], type: field["type"]} - end) + |> Enum.map(&to_property/1) else error -> error end @@ -581,4 +579,24 @@ defmodule ExForce.API do defp untargeted_object?(object), do: String.contains?(object["name"], "Userpilot") + + defp to_object(object, _type) do + %{ + fully_qualified_name: object["name"], + singular_name: object["label"], + plural_name: object["labelPlural"], + primary_object_id: object["name"], + is_standard_object: not object["custom"], + is_custom_object: object["custom"] + } + end + + defp to_property(field) do + %{ + title: field["label"], + id: field["name"], + type: field["type"], + is_custom_property: field["custom"] + } + end end From f0c9666a52378f056bf067eb1474b8a361a16fb5 Mon Sep 17 00:00:00 2001 From: Waseem Awashra Date: Tue, 2 Sep 2025 19:53:02 +0300 Subject: [PATCH 3/5] Combine Contact and Lead objects --- lib/api.ex | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/api.ex b/lib/api.ex index cef0f53..cf671af 100644 --- a/lib/api.ex +++ b/lib/api.ex @@ -90,6 +90,7 @@ defmodule ExForce.API do |> Enum.filter(&targeted_object?/1) |> Enum.reject(&untargeted_object?/1) |> Enum.map(&to_object(&1, :standard_object)) + |> merge_contact_and_lead_objects() else {:error, error} -> {:error, error} @@ -599,4 +600,25 @@ defmodule ExForce.API do is_custom_property: field["custom"] } end + + defp merge_contact_and_lead_objects(objects) do + contact_object = + objects |> Enum.find(fn object -> object[:fully_qualified_name] == "Contact" end) + + objects + |> Enum.reject(fn object -> + object[:fully_qualified_name] == "Lead" or object[:fully_qualified_name] == "Contact" + end) + |> then(fn objects -> + [ + %{ + contact_object + | fully_qualified_name: "Contact/Lead", + primary_object_id: "Contact/Lead", + singular_name: "Contact/Lead", + plural_name: "Contacts/Leads" + } + ] ++ objects + end) + end end From 491ee68847a035242cc3fe072c34bfa70102f6e7 Mon Sep 17 00:00:00 2001 From: Waseem Awashra Date: Wed, 3 Sep 2025 09:52:59 +0300 Subject: [PATCH 4/5] Fix spec --- lib/api.ex | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/api.ex b/lib/api.ex index cf671af..1afce7c 100644 --- a/lib/api.ex +++ b/lib/api.ex @@ -81,7 +81,6 @@ defmodule ExForce.API do def refresh_app_client(config, :salesforce_kb) do SalesforceKB.refresh_app_token(config) end - @spec get_available_objects(binary()) :: {:ok, list()} | {:error, any()} def get_available_objects(app_token) do with {:ok, client} <- get_client(app_token), @@ -91,6 +90,13 @@ defmodule ExForce.API do |> Enum.reject(&untargeted_object?/1) |> Enum.map(&to_object(&1, :standard_object)) |> merge_contact_and_lead_objects() + |> case do + objects when is_list(objects) -> + {:ok, objects} + + _ -> + {:error, "No available objects"} + end else {:error, error} -> {:error, error} @@ -106,6 +112,7 @@ defmodule ExForce.API do |> Enum.filter(fn object -> object["custom"] == true end) |> Enum.reject(fn object -> String.contains?(object["name"], "Userpilot") end) |> Enum.map(&to_object(&1, :custom_object)) + |> then(&{:ok, &1}) else {:error, error} -> {:error, error} From d2826d4fc3807d58f85099dd225f8346d2a69707 Mon Sep 17 00:00:00 2001 From: Waseem Awashra Date: Wed, 22 Oct 2025 11:26:05 +0300 Subject: [PATCH 5/5] add eventable on objects --- lib/api.ex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/api.ex b/lib/api.ex index 1afce7c..e5bf7f2 100644 --- a/lib/api.ex +++ b/lib/api.ex @@ -81,6 +81,7 @@ defmodule ExForce.API do def refresh_app_client(config, :salesforce_kb) do SalesforceKB.refresh_app_token(config) end + @spec get_available_objects(binary()) :: {:ok, list()} | {:error, any()} def get_available_objects(app_token) do with {:ok, client} <- get_client(app_token), @@ -590,6 +591,7 @@ defmodule ExForce.API do defp to_object(object, _type) do %{ + eventable: true, fully_qualified_name: object["name"], singular_name: object["label"], plural_name: object["labelPlural"],