defmodule BerrypodWeb.Admin.Providers.Form do use BerrypodWeb, :live_view alias Berrypod.Products alias Berrypod.Products.ProviderConnection alias Berrypod.Providers @supported_types ~w(printify printful) @impl true def mount(params, _session, socket) do {:ok, apply_action(socket, socket.assigns.live_action, params)} end defp apply_action(socket, :new, params) do provider_type = validated_type(params["type"]) socket |> assign(:page_title, "Connect to #{provider_label(provider_type)}") |> assign(:provider_type, provider_type) |> assign(:connection, %ProviderConnection{provider_type: provider_type}) |> assign(:form, to_form(ProviderConnection.changeset(%ProviderConnection{}, %{}))) |> assign(:testing, false) |> assign(:test_result, nil) |> assign(:pending_api_key, nil) end defp apply_action(socket, :edit, %{"id" => id}) do connection = Products.get_provider_connection!(id) socket |> assign(:page_title, "#{provider_label(connection.provider_type)} settings") |> assign(:provider_type, connection.provider_type) |> assign(:connection, connection) |> assign(:form, to_form(ProviderConnection.changeset(connection, %{}))) |> assign(:testing, false) |> assign(:test_result, nil) |> assign(:pending_api_key, nil) end @impl true def handle_event("validate", %{"provider_connection" => params}, socket) do form = socket.assigns.connection |> ProviderConnection.changeset(params) |> Map.put(:action, :validate) |> to_form() # Store api_key separately since changeset encrypts it immediately {:noreply, assign(socket, form: form, pending_api_key: params["api_key"])} end @impl true def handle_event("test_connection", _params, socket) do socket = assign(socket, testing: true, test_result: nil) api_key = socket.assigns[:pending_api_key] || ProviderConnection.get_api_key(socket.assigns.connection) if api_key && api_key != "" do temp_conn = %ProviderConnection{ provider_type: socket.assigns.provider_type, api_key_encrypted: encrypt_api_key(api_key) } result = Providers.test_connection(temp_conn) {:noreply, assign(socket, testing: false, test_result: result)} else {:noreply, assign(socket, testing: false, test_result: {:error, :no_api_key})} end end @impl true def handle_event("save", %{"provider_connection" => params}, socket) do save_connection(socket, socket.assigns.live_action, params) end defp save_connection(socket, :new, params) do provider_type = socket.assigns.provider_type params = params |> Map.put("provider_type", provider_type) |> maybe_add_config(provider_type, socket.assigns.test_result) |> maybe_add_name(provider_type, socket.assigns.test_result) case Products.create_provider_connection(params) do {:ok, _connection} -> {:noreply, socket |> put_flash(:info, "Connected to #{provider_label(provider_type)}!") |> push_navigate(to: ~p"/admin/settings")} {:error, %Ecto.Changeset{} = changeset} -> {:noreply, assign(socket, form: to_form(changeset))} end end defp save_connection(socket, :edit, params) do case Products.update_provider_connection(socket.assigns.connection, params) do {:ok, _connection} -> {:noreply, socket |> put_flash(:info, "Settings saved") |> push_navigate(to: ~p"/admin/settings")} {:error, %Ecto.Changeset{} = changeset} -> {:noreply, assign(socket, form: to_form(changeset))} end end # Printify returns shop_id, Printful returns store_id defp maybe_add_config(params, "printify", {:ok, %{shop_id: shop_id}}) do config = Map.get(params, "config", %{}) |> Map.put("shop_id", to_string(shop_id)) Map.put(params, "config", config) end defp maybe_add_config(params, "printful", {:ok, %{store_id: store_id}}) do config = Map.get(params, "config", %{}) |> Map.put("store_id", to_string(store_id)) Map.put(params, "config", config) end defp maybe_add_config(params, _type, _result), do: params defp maybe_add_name(params, "printify", {:ok, %{shop_name: name}}) when is_binary(name) do Map.put_new(params, "name", name) end defp maybe_add_name(params, "printful", {:ok, %{store_name: name}}) when is_binary(name) do Map.put_new(params, "name", name) end defp maybe_add_name(params, type, _result) do Map.put_new(params, "name", provider_label(type)) end defp encrypt_api_key(api_key) do case Berrypod.Vault.encrypt(api_key) do {:ok, encrypted} -> encrypted _ -> nil end end defp validated_type(type) when type in @supported_types, do: type defp validated_type(_), do: "printify" # Shared helpers used by the template defp provider_label("printful"), do: "Printful" defp provider_label(_), do: "Printify" defp connection_name({:ok, %{shop_name: name}}), do: name defp connection_name({:ok, %{store_name: name}}), do: name defp connection_name(_), do: nil defp format_error(:no_api_key), do: "Please enter your API key" defp format_error(:unauthorized), do: "That key doesn't seem to be valid" defp format_error(:timeout), do: "Couldn't reach the provider - try again" defp format_error({:http_error, _code}), do: "Something went wrong - try again" defp format_error(error) when is_binary(error), do: error defp format_error(_), do: "Connection failed - check your key and try again" end