defmodule Mix.Tasks.Compile.Forcex do use Mix.Task @recursive false def run(_) do {:ok, _} = Application.ensure_all_started(:forcex) client = Forcex.Client.login case client do %{access_token: nil} -> IO.puts("Invalid configuration/credentials. Cannot generate SObjects.") _ -> generate_modules(client) end :ok end defp generate_modules(client) do client = Forcex.Client.locate_services(client) sobjects = client |> Forcex.describe_global |> Map.get(:sobjects) for sobject <- sobjects do sobject |> generate_module(client) |> Code.compile_quoted end end defp generate_module(sobject, client) do name = sobject.name urls = sobject.urls describe_url = urls.describe sobject_url = urls.sobject row_template_url = urls.rowTemplate full_description = Forcex.describe_sobject(name, client) quote location: :keep do defmodule unquote(Module.concat(Forcex.SObject, name)) do @moduledoc """ Dynamically generated module for `#{unquote(full_description.label)}` ## Fields #{unquote(for field <- full_description.fields, do: docs_for_field(field))} """ @doc """ Retrieves extended metadata for `#{unquote(name)}` See [SObject Describe](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_describe.htm) """ def describe(client) do unquote(describe_url) |> Forcex.get(client) end @doc """ Retrieves basic metadata for `#{unquote(name)}` See [SObject Basic Information](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_basic_info.htm) """ def basic_info(client) do unquote(sobject_url) |> Forcex.get(client) end @doc """ Create a new `#{unquote(name)}` Parameters * `sobject` - a map of key/value pairs See [SObject Basic Information](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_basic_info.htm) """ def create(sobject, client) when is_map(sobject) do unquote(sobject_url) |> Forcex.post(sobject, client) end @doc """ Update an existing `#{unquote(name)}` Parameters * `id` - 18 character SFDC identifier. * `changeset` - map of key/value pairs *only* of elements changing See [SObject Rows](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_retrieve.htm) """ def update(id, changeset, client) do unquote(row_template_url) |> String.replace("{ID}", id) |> Forcex.patch(changeset, client) end @doc """ Delete an existing `#{unquote(name)}` Parameters * `id` - 18 character SFDC identifier See [SObject Rows](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_retrieve.htm) """ def delete(id, client) do unquote(row_template_url) |> String.replace("{ID}", id) |> Forcex.delete(client) end @doc """ Retrieve an existing `#{unquote(name)}` Parameters * `id` - 18 character SFDC identifier See [SObject Rows](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_retrieve.htm) """ def get(id, client) do unquote(row_template_url) |> String.replace("{ID}", id) |> Forcex.get(client) end @doc """ Retrieve the IDs of `#{unquote(name)}`s deleted between `start_date` and `end_date` Parameters * `start_date` - `Timex.Convertable` or ISO8601 string * `end_date` - `Timex.Convertable` or ISO8601 string See [SObject Get Deleted](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_getdeleted.htm) """ def deleted_between(start_date, end_date, client) when is_binary(start_date) and is_binary(end_date) do params = %{"start" => start_date, "end" => end_date} |> URI.encode_query unquote(sobject_url) <> "/deleted?#{params}" |> Forcex.get(client) end def deleted_between(start_date, end_date, client) do deleted_between( Timex.format!(start_date, "{ISO8601z}"), Timex.format!(end_date, "{ISO8601z}"), client) end @doc """ Retrieve the IDs of `#{unquote(name)}`s updated between `start_date` and `end_date` Parameters * `start_date` - `Timex.Convertable` or ISO8601 string * `end_date` - `Timex.Convertable` or ISO8601 string See [SObject Get Updated](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_getupdated.htm) """ def updated_between(start_date, end_date, client) when is_binary(start_date) and is_binary(end_date) do params = %{"start" => start_date, "end" => end_date} |> URI.encode_query unquote(sobject_url) <> "/updated?#{params}" |> Forcex.get(client) end def updated_between(start_date, end_date, client) do updated_between( Timex.format!(start_date, "{ISO}"), Timex.format!(end_date, "{ISO}"), client) end @doc """ Retrieve a binary field in `#{unquote(name)}` Parameters * `id` - 18 character SFDC identifier * `field` - name of field with binary contents See [SObject Blob Retrieve](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_blob_retrieve.htm) """ def get_blob(id, field, client) do unquote(row_template_url) <> "/#{field}" |> String.replace("{ID}", id) |> Forcex.get(client) end @doc """ Retrieve `#{unquote(name)}` records based on external field `field` having value `value` Parameters * `field` - name of external field * `value` - value of `field` for desired records See [SObject Rows by External ID](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_upsert.htm) """ def by_external(field, value, client) do unquote(sobject_url) <> "/#{field}/#{value}" |> Forcex.get(client) end end IO.puts "Generated #{unquote(Module.concat(Forcex.SObject, name))}" end end defp docs_for_field(%{name: name, type: type, label: label, picklistValues: values}) when type in [:picklist, :multipicklist] do """ * `#{name}` - `#{type}`, #{label} #{for value <- values, do: docs_for_picklist_values(value)} """ end defp docs_for_field(%{name: name, type: type, label: label}) do "* `#{name}` - `#{type}`, #{label}\n" end defp docs_for_picklist_values(%{value: value, active: true}) do " * `#{value}`\n" end defp docs_for_picklist_values(_), do: "" end