diff --git a/README.md b/README.md index ee94158..ef2d1c2 100644 --- a/README.md +++ b/README.md @@ -303,6 +303,27 @@ defmodule People do end ``` +## Configure Altnertive Credentials + +An index might need a different application_id / api_key. To accomplish this you can use the credentials configuration (each entry should be a `{application_id, api_key}` tuple). + +```elixir +config :algoliax, + api_key: System.get_env("ALGOLIA_API_KEY"), + application_id: System.get_env("ALGOLIA_APPLICATION_ID) + credentials: %{ + custom_1: {"", ""}, + custom_2: {System.get_env("ALGOLIA_APPLICATION_ID_2"), System.get_env("ALGOLIA_API_KEY_2")}, + } + +defmodule People do + use Algoliax.Indexer, + #.... + credentials: :custom_1 +end + +If you do not specify a credentials key the default api_key / application_id on the `:algoliax` configuration will be used. + ## Copyright and License Copyright (c) 2020 CORUSCANT (welcome to the jungle) - diff --git a/config/test.exs b/config/test.exs index 58cddbf..dcbdcab 100644 --- a/config/test.exs +++ b/config/test.exs @@ -4,7 +4,10 @@ config :algoliax, application_id: "APPLICATION_ID", api_key: "api_key", batch_size: 1, - ecto_repos: [Algoliax.Repo] + ecto_repos: [Algoliax.Repo], + credentials: %{ + custom_1: {"APPLICATION_ID_1", "api_key_1"} + } config :algoliax, Algoliax.Repo, pool: Ecto.Adapters.SQL.Sandbox, diff --git a/lib/algoliax/client.ex b/lib/algoliax/client.ex index 86629e4..4623a8e 100644 --- a/lib/algoliax/client.ex +++ b/lib/algoliax/client.ex @@ -3,7 +3,7 @@ defmodule Algoliax.Client do require Logger - alias Algoliax.{Config, Routes} + alias Algoliax.Routes def request(request, retry \\ 0) @@ -17,7 +17,7 @@ defmodule Algoliax.Client do log(action, method, url, body) method - |> :hackney.request(url, request_headers(), Jason.encode!(body), [ + |> :hackney.request(url, request_headers(request), Jason.encode!(body), [ :with_body, recv_timeout: recv_timeout() ]) @@ -50,16 +50,16 @@ defmodule Algoliax.Client do defp build_response(response, request) do case Jason.decode(response) do - {:ok, response} -> Algoliax.Response.new(response, request[:url_params]) + {:ok, response} -> Algoliax.Response.new(response, request) error -> error end end - defp request_headers do + defp request_headers(request) do [ {"Content-type", "application/json"}, - {"X-Algolia-API-Key", Config.api_key()}, - {"X-Algolia-Application-Id", Config.application_id()} + {"X-Algolia-API-Key", request.api_key}, + {"X-Algolia-Application-Id", request.application_id} ] ++ x_forwarded_for() end diff --git a/lib/algoliax/config.ex b/lib/algoliax/config.ex index d7d0821..729291e 100644 --- a/lib/algoliax/config.ex +++ b/lib/algoliax/config.ex @@ -5,10 +5,30 @@ defmodule Algoliax.Config do Application.get_env(:algoliax, :api_key) end + def api_key(credenital_name) do + :algoliax + |> Application.get_env(:credentials, []) + |> Map.fetch(credenital_name) + |> then(fn + {:ok, {_application_id, api_key}} -> api_key + :error -> raise Algoliax.InvalidAlgoliaCredentialsError, %{name: inspect(credenital_name)} + end) + end + def application_id do Application.get_env(:algoliax, :application_id) end + def application_id(credenital_name) do + :algoliax + |> Application.get_env(:credentials, []) + |> Map.fetch(credenital_name) + |> then(fn + {:ok, {application_id, _api_key}} -> application_id + :error -> raise Algoliax.InvalidAlgoliaCredentialsError, %{name: inspect(credenital_name)} + end) + end + def cursor_field do Application.get_env(:algoliax, :cursor_field) end diff --git a/lib/algoliax/exceptions.ex b/lib/algoliax/exceptions.ex index 290be4b..3b2691c 100644 --- a/lib/algoliax/exceptions.ex +++ b/lib/algoliax/exceptions.ex @@ -44,6 +44,19 @@ defmodule Algoliax.InvalidAlgoliaSettingsFunctionError do end end +defmodule Algoliax.InvalidAlgoliaCredentialsError do + @moduledoc "Raise when `:crendentials` are invalid" + + defexception [:message] + + @impl true + def exception(%{name: name}) do + %__MODULE__{ + message: "Expected #{name} to be configuired in alogila credentials" + } + end +end + defmodule Algoliax.InvalidAlgoliaSettingsConfigurationError do @moduledoc "Raise when the `:algolia` settings are unsupported" diff --git a/lib/algoliax/indexer.ex b/lib/algoliax/indexer.ex index 26077ad..684e46d 100644 --- a/lib/algoliax/indexer.ex +++ b/lib/algoliax/indexer.ex @@ -10,6 +10,7 @@ defmodule Algoliax.Indexer do - `:schemas`: Specify which schemas used to populate index, Default: `[__CALLER__]` - `:default_filters`: Specify default filters to be used when reindex without providing a query. Must be a map or a function name (that returns a map). Default: `%{}`. - `:algolia`: Any valid Algolia settings (using snake case or camel case, ie `attributeForFaceting` can be configured with `:attribute_for_faceting`) or the name of 0-arity function that returns those settings. + - `:credentials`: Specify an alternative application_id / api_key for this index. Default `nil` On first call to Algolia, we check that the settings on Algolia are up to date. @@ -157,6 +158,29 @@ defmodule Algoliax.Indexer do [attribute_for_faceting: ["age"]] end end + + ### Alternative Credentials + + An index might need a different application_id / api_key. To accomplish this you can use the credentials configuration. + + ```elixir + config :algoliax, + api_key: System.get_env("ALGOLIA_API_KEY"), + application_id: System.get_env("ALGOLIA_APPLICATION_ID) + credentials: %{ + custom_1: {"", ""}, + custom_2: {System.get_env("ALGOLIA_APPLICATION_ID_2"), System.get_env("ALGOLIA_API_KEY_2")}, + } + + defmodule People do + use Algoliax.Indexer, + #.... + credentials: :custom_1 + end + + If you do not specify a credentials key the default api_key / application_id on the `:algoliax` configuration will be used. + ``` + """ alias Algoliax.Resources.{Index, Object, Search} @@ -347,7 +371,7 @@ defmodule Algoliax.Indexer do > NOTE: filters as Map supports only `:where` and equality """ @callback reindex(query :: Ecto.Query.t(), opts :: Keyword.t()) :: - {:ok, [Algoliax.Response.t()]} | {:ok, list(Algoliax.Responses.t())} + {:ok, [{:ok, Algoliax.Response.t()}]} | {:ok, list(Algoliax.Responses.t())} @doc """ Reindex all objects ([Ecto](https://hexdocs.pm/ecto/Ecto.html) specific) @@ -361,7 +385,7 @@ defmodule Algoliax.Indexer do - `:force_delete`: delete objects where `to_be_indexed?` is `false` """ @callback reindex(opts :: Keyword.t()) :: - {:ok, [Algoliax.Response.t()]} | {:ok, list(Algoliax.Responses.t())} + {:ok, [{:ok, Algoliax.Response.t()}]} | {:ok, list(Algoliax.Responses.t())} @doc """ Reindex atomically ([Ecto](https://hexdocs.pm/ecto/Ecto.html) specific) diff --git a/lib/algoliax/resources/index.ex b/lib/algoliax/resources/index.ex index 0f60c32..d3b3e9f 100644 --- a/lib/algoliax/resources/index.ex +++ b/lib/algoliax/resources/index.ex @@ -1,9 +1,17 @@ defmodule Algoliax.Resources.Index do @moduledoc false - import Algoliax.Utils, only: [index_name: 2, algolia_settings: 2, render_response: 1] import Algoliax.Client, only: [request: 1] + import Algoliax.Utils, + only: [ + algolia_settings: 2, + api_key: 1, + application_id: 1, + index_name: 2, + render_response: 1 + ] + alias Algoliax.{Settings, SettingsStore} def ensure_settings(module, index_name, settings, replica_index) do @@ -11,10 +19,11 @@ defmodule Algoliax.Resources.Index do nil -> request_configure_index( index_name, + settings, settings_to_algolia_settings(module, settings, replica_index) ) - algolia_remote_settings = request_get_settings(index_name) + algolia_remote_settings = request_get_settings(index_name, settings) SettingsStore.set_settings(index_name, algolia_remote_settings) replicas_names(module, settings, replica_index) @@ -94,7 +103,7 @@ defmodule Algoliax.Resources.Index do def get_settings(module, settings) do index_name(module, settings) |> Enum.map(fn index_name -> - algolia_remote_settings = request_get_settings(index_name) + algolia_remote_settings = request_get_settings(index_name, settings) SettingsStore.set_settings(index_name, algolia_remote_settings) algolia_remote_settings end) @@ -108,6 +117,7 @@ defmodule Algoliax.Resources.Index do r = request_configure_index( index_name, + settings, settings_to_algolia_settings(module, settings, replica_index) ) @@ -125,25 +135,43 @@ defmodule Algoliax.Resources.Index do end) end - defp request_configure_index(index_name, settings) do + defp request_configure_index(index_name, settings, alogolia_settings) do + api_key = api_key(settings) + application_id = application_id(settings) + request(%{ action: :configure_index, - url_params: [index_name: index_name], - body: settings + url_params: [index_name: index_name, application_id: application_id], + body: alogolia_settings, + api_key: api_key, + application_id: application_id }) end - defp request_get_settings(index_name) do + defp request_get_settings(index_name, settings) do + api_key = api_key(settings) + application_id = application_id(settings) + request(%{ action: :get_settings, - url_params: [index_name: index_name] + url_params: [index_name: index_name, application_id: application_id], + api_key: api_key, + application_id: application_id }) end def delete_index(module, settings) do + api_key = api_key(settings) + application_id = application_id(settings) + index_name(module, settings) |> Enum.map(fn index_name -> - request(%{action: :delete_index, url_params: [index_name: index_name]}) + request(%{ + action: :delete_index, + url_params: [index_name: index_name, application_id: application_id], + api_key: api_key, + application_id: application_id + }) end) |> render_response() end diff --git a/lib/algoliax/resources/object.ex b/lib/algoliax/resources/object.ex index ead9ea7..81cfe26 100644 --- a/lib/algoliax/resources/object.ex +++ b/lib/algoliax/resources/object.ex @@ -1,26 +1,43 @@ defmodule Algoliax.Resources.Object do @moduledoc false - import Algoliax.Utils, only: [index_name: 2, object_id_attribute: 1, render_response: 1] import Algoliax.Client, only: [request: 1] + import Algoliax.Utils, + only: [ + api_key: 1, + application_id: 1, + index_name: 2, + object_id_attribute: 1, + render_response: 1 + ] + alias Algoliax.TemporaryIndexer def get_object(module, settings, model) do + api_key = api_key(settings) + application_id = application_id(settings) + index_name(module, settings) |> Enum.map(fn index_name -> request(%{ action: :get_object, url_params: [ index_name: index_name, - object_id: get_object_id(module, settings, model) - ] + object_id: get_object_id(module, settings, model), + application_id: application_id + ], + api_key: api_key, + application_id: application_id }) end) |> render_response() end def save_objects(module, settings, models, opts) do + api_key = api_key(settings) + application_id = application_id(settings) + objects = index_name(module, settings) |> Enum.reduce(%{}, fn index_name, acc -> @@ -36,8 +53,13 @@ defmodule Algoliax.Resources.Object do |> Enum.map(fn {index_name, objects} -> request(%{ action: :save_objects, - url_params: [index_name: index_name], - body: %{requests: objects} + url_params: [ + index_name: index_name, + application_id: application_id + ], + body: %{requests: objects}, + api_key: api_key, + application_id: application_id }) end) |> render_response() @@ -65,13 +87,22 @@ defmodule Algoliax.Resources.Object do defp save_object(module, settings, model, index_name) do if apply(module, :to_be_indexed?, [model]) do + api_key = api_key(settings) + application_id = application_id(settings) + object = build_object(module, settings, model, index_name) call_indexer(:save_object, module, settings, model) request(%{ action: :save_object, - url_params: [index_name: index_name, object_id: object.objectID], - body: object + url_params: [ + index_name: index_name, + object_id: object.objectID, + application_id: application_id + ], + body: object, + api_key: api_key, + application_id: application_id }) else {:not_indexable, model} @@ -79,6 +110,9 @@ defmodule Algoliax.Resources.Object do end def delete_object(module, settings, model) do + api_key = api_key(settings) + application_id = application_id(settings) + call_indexer(:delete_object, module, settings, model) index_name(module, settings) @@ -87,14 +121,20 @@ defmodule Algoliax.Resources.Object do action: :delete_object, url_params: [ index_name: index_name, - object_id: get_object_id(module, settings, model) - ] + object_id: get_object_id(module, settings, model), + application_id: application_id + ], + api_key: api_key, + application_id: application_id }) end) |> render_response() end def delete_by(module, settings, matching_filter) do + api_key = api_key(settings) + application_id = application_id(settings) + call_indexer(:delete_by, module, settings, matching_filter) body = @@ -111,9 +151,12 @@ defmodule Algoliax.Resources.Object do request(%{ action: :delete_by, url_params: [ - index_name: index_name + index_name: index_name, + application_id: application_id ], - body: body + body: body, + api_key: api_key, + application_id: application_id }) end) |> render_response() diff --git a/lib/algoliax/resources/object_ecto.ex b/lib/algoliax/resources/object_ecto.ex index 943cb3b..6f08cc8 100644 --- a/lib/algoliax/resources/object_ecto.ex +++ b/lib/algoliax/resources/object_ecto.ex @@ -4,7 +4,15 @@ if Code.ensure_loaded?(Ecto) do import Ecto.Query import Algoliax.Client, only: [request: 1] - import Algoliax.Utils, only: [index_name: 2, schemas: 2, default_filters: 2] + + import Algoliax.Utils, + only: [ + api_key: 1, + application_id: 1, + default_filters: 2, + index_name: 2, + schemas: 2 + ] alias Algoliax.Resources.Object @@ -77,6 +85,9 @@ if Code.ensure_loaded?(Ecto) do # sobelow_skip ["DOS.BinToAtom"] def reindex_atomic(module, settings) do + api_key = api_key(settings) + application_id = application_id(settings) + Algoliax.UtilsEcto.repo(settings) index_name(module, settings) @@ -93,11 +104,16 @@ if Code.ensure_loaded?(Ecto) do request(%{ action: :move_index, - url_params: [index_name: tmp_index_name], + url_params: [ + index_name: tmp_index_name, + application_id: application_id + ], body: %{ operation: "move", destination: "#{index_name}" - } + }, + api_key: api_key, + application_id: application_id }) {:ok, :completed} diff --git a/lib/algoliax/resources/search.ex b/lib/algoliax/resources/search.ex index 6b9fbda..f12750a 100644 --- a/lib/algoliax/resources/search.ex +++ b/lib/algoliax/resources/search.ex @@ -1,9 +1,21 @@ defmodule Algoliax.Resources.Search do @moduledoc false - import Algoliax.Utils, only: [index_name: 2, camelize: 1, render_response: 1] + import Algoliax.Client, only: [request: 1] + import Algoliax.Utils, + only: [ + api_key: 1, + application_id: 1, + camelize: 1, + index_name: 2, + render_response: 1 + ] + def search(module, settings, query, params) do + api_key = api_key(settings) + application_id = application_id(settings) + index_name(module, settings) |> Enum.map(fn index_name -> body = @@ -14,14 +26,22 @@ defmodule Algoliax.Resources.Search do request(%{ action: :search, - url_params: [index_name: index_name], - body: body + url_params: [ + index_name: index_name, + application_id: application_id + ], + body: body, + api_key: api_key, + application_id: application_id }) end) |> render_response() end def search_facet(module, settings, facet_name, facet_query, params) do + api_key = api_key(settings) + application_id = application_id(settings) + index_name(module, settings) |> Enum.map(fn index_name -> body = @@ -36,8 +56,14 @@ defmodule Algoliax.Resources.Search do request(%{ action: :search_facet, - url_params: [index_name: index_name, facet_name: facet_name], - body: body + url_params: [ + index_name: index_name, + facet_name: facet_name, + application_id: application_id + ], + body: body, + api_key: api_key, + application_id: application_id }) end) |> render_response() diff --git a/lib/algoliax/resources/task.ex b/lib/algoliax/resources/task.ex index f50b4e5..f882158 100644 --- a/lib/algoliax/resources/task.ex +++ b/lib/algoliax/resources/task.ex @@ -10,8 +10,11 @@ defmodule Algoliax.Resources.Task do action: :task, url_params: [ index_name: index_name, - task_id: response.task_id - ] + task_id: response.task_id, + application_id: response.application_id + ], + api_key: response.api_key, + application_id: response.application_id }) end end diff --git a/lib/algoliax/response.ex b/lib/algoliax/response.ex index ac5d4da..164b3c2 100644 --- a/lib/algoliax/response.ex +++ b/lib/algoliax/response.ex @@ -4,20 +4,31 @@ defmodule Algoliax.Response do """ @type t :: %__MODULE__{ + api_key: binary(), + application_id: binary(), + params: keyword(), response: map(), - task_id: integer(), - updated_at: binary(), - params: keyword() + task_id: integer() | nil, + updated_at: binary() | nil } - defstruct [:response, :task_id, :updated_at, :params] + defstruct [ + :api_key, + :application_id, + :params, + :response, + :task_id, + :updated_at + ] - def new(response, params) do + def new(response, request) do response = %__MODULE__{ + api_key: request.api_key, + application_id: request.application_id, + params: request.url_params, response: response, task_id: response["taskID"], - updated_at: response["updatedAt"], - params: params + updated_at: response["updatedAt"] } {:ok, response} diff --git a/lib/algoliax/routes.ex b/lib/algoliax/routes.ex index cb2e6e1..30cc63b 100644 --- a/lib/algoliax/routes.ex +++ b/lib/algoliax/routes.ex @@ -1,6 +1,5 @@ defmodule Algoliax.Routes do @moduledoc false - alias Algoliax.Config @paths %{ search: {"/{index_name}/query", :post}, @@ -23,14 +22,15 @@ defmodule Algoliax.Routes do |> Map.get(action) url = - action_path - |> build_path(url_params) - |> build_url(method, retry) + method + |> build_url(retry) + |> Kernel.<>(action_path) + |> interpolate_path(url_params) {method, url} end - defp build_path(path, args) do + defp interpolate_path(path, args) do args |> Keyword.keys() |> Enum.reduce(path, fn key, path -> @@ -39,51 +39,45 @@ defmodule Algoliax.Routes do end) end - defp build_url(path, :get, 0) do + defp build_url(:get, 0) do url_read() - |> String.replace(~r/{{application_id}}/, Config.application_id()) - |> Kernel.<>(path) end - defp build_url(path, _method, 0) do + defp build_url(_method, 0) do url_write() - |> String.replace(~r/{{application_id}}/, Config.application_id()) - |> Kernel.<>(path) end - defp build_url(path, _method, retry) do + defp build_url(_method, retry) do url_retry() - |> String.replace(~r/{{application_id}}/, Config.application_id()) |> String.replace(~r/{{retry}}/, to_string(retry)) - |> Kernel.<>(path) end if Mix.env() == :test do defp url_read do port = Application.get_env(:algoliax, :mock_api_port) - "http://localhost:#{port}/{{application_id}}/read" + "http://localhost:#{port}/{application_id}/read" end defp url_write do port = Application.get_env(:algoliax, :mock_api_port) - "http://localhost:#{port}/{{application_id}}/write" + "http://localhost:#{port}/{application_id}/write" end defp url_retry do port = Application.get_env(:algoliax, :mock_api_port) - "http://localhost:#{port}/{{application_id}}/retry/{{retry}}" + "http://localhost:#{port}/{application_id}/retry/{{retry}}" end else defp url_read do - "https://{{application_id}}-dsn.algolia.net/1/indexes" + "https://{application_id}-dsn.algolia.net/1/indexes" end defp url_write do - "https://{{application_id}}.algolia.net/1/indexes" + "https://{application_id}.algolia.net/1/indexes" end defp url_retry do - "https://{{application_id}}-{{retry}}.algolianet.com/1/indexes" + "https://{application_id}-{{retry}}.algolianet.com/1/indexes" end end end diff --git a/lib/algoliax/utils.ex b/lib/algoliax/utils.ex index 5d07c26..9658bc8 100644 --- a/lib/algoliax/utils.ex +++ b/lib/algoliax/utils.ex @@ -1,8 +1,31 @@ defmodule Algoliax.Utils do @moduledoc false + alias Algoliax.Config alias Algoliax.Resources.Index + @spec api_key(keyword()) :: binary() + def api_key(settings) do + case Keyword.get(settings, :credentials) do + nil -> + Config.api_key() + + atom when is_atom(atom) -> + Config.api_key(atom) + end + end + + @spec application_id(keyword()) :: binary() + def application_id(settings) do + case Keyword.get(settings, :credentials) do + nil -> + Config.application_id() + + atom when is_atom(atom) -> + Config.application_id(atom) + end + end + def index_name(module, settings) do indexes = case Keyword.get(settings, :index_name) do diff --git a/test/algoliax/client_test.exs b/test/algoliax/client_test.exs index cae57d5..a02b902 100644 --- a/test/algoliax/client_test.exs +++ b/test/algoliax/client_test.exs @@ -2,10 +2,20 @@ defmodule Algoliax.ClientTest do use Algoliax.RequestCase test "test retries" do - Application.put_env(:algoliax, :api_key, "api_key") + api_key = "api_key" + application_id = "APPLICATION_ID" Algoliax.Client.request( - %{action: :get_object, url_params: [index_name: :index_name, object_id: "error"]}, + %{ + action: :get_object, + api_key: api_key, + application_id: application_id, + url_params: [ + application_id: application_id, + index_name: :index_name, + object_id: "error" + ] + }, 0 ) @@ -16,7 +26,8 @@ defmodule Algoliax.ClientTest do end test "Error http" do - Application.put_env(:algoliax, :api_key, "api_key_invalid") + api_key = "api_key_invalid" + application_id = "APPLICATION_ID" message = """ Algolia HTTP error: @@ -28,7 +39,13 @@ defmodule Algoliax.ClientTest do Algoliax.Client.request( %{ action: :get_object, - url_params: [index_name: :index_name_not_authorized, object_id: "Whatever"] + api_key: api_key, + application_id: application_id, + url_params: [ + application_id: application_id, + index_name: :index_name_not_authorized, + object_id: "Whatever" + ] }, 0 ) diff --git a/test/algoliax/replica_test.exs b/test/algoliax/replica_test.exs index 20dc737..4660ee6 100644 --- a/test/algoliax/replica_test.exs +++ b/test/algoliax/replica_test.exs @@ -5,6 +5,9 @@ defmodule AlgoliaxTest.ReplicaTest do alias Algoliax.Schemas.PeopleWithReplicasMultipleIndexes alias Algoliax.Schemas.PeopleWithInvalidReplicas + @application_id "APPLICATION_ID" + @api_key "api_key" + setup do Algoliax.SettingsStore.set_settings(:algoliax_people_replicas, %{}) Algoliax.SettingsStore.set_settings(:algoliax_people_replicas_en, %{}) @@ -56,7 +59,12 @@ defmodule AlgoliaxTest.ReplicaTest do {:ok, %Algoliax.Response{ response: %{"taskID" => _, "updatedAt" => _}, - params: [index_name: :algoliax_people_replicas_en] + params: [ + index_name: :algoliax_people_replicas_en, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res @@ -67,7 +75,12 @@ defmodule AlgoliaxTest.ReplicaTest do {:ok, %Algoliax.Response{ response: %{"taskID" => _, "updatedAt" => _}, - params: [index_name: :algoliax_people_replicas_fr] + params: [ + index_name: :algoliax_people_replicas_fr, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res2 @@ -200,7 +213,11 @@ defmodule AlgoliaxTest.ReplicaTest do {:ok, %Algoliax.Response{ response: %{"taskID" => _, "updatedAt" => _, "objectID" => ^reference}, - params: [index_name: :algoliax_people_replicas_en, object_id: ^reference] + params: [ + index_name: :algoliax_people_replicas_en, + object_id: ^reference, + application_id: @application_id + ] }} ] } = res @@ -211,7 +228,11 @@ defmodule AlgoliaxTest.ReplicaTest do {:ok, %Algoliax.Response{ response: %{"taskID" => _, "updatedAt" => _, "objectID" => ^reference}, - params: [index_name: :algoliax_people_replicas_fr, object_id: ^reference] + params: [ + index_name: :algoliax_people_replicas_fr, + object_id: ^reference, + application_id: @application_id + ] }} ] } = res2 @@ -301,7 +322,12 @@ defmodule AlgoliaxTest.ReplicaTest do "taskID" => _, "objectIDs" => [^reference1, ^reference2] }, - params: [index_name: :algoliax_people_replicas_en] + params: [ + index_name: :algoliax_people_replicas_en, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res @@ -315,7 +341,12 @@ defmodule AlgoliaxTest.ReplicaTest do "taskID" => _, "objectIDs" => [^reference1, ^reference2] }, - params: [index_name: :algoliax_people_replicas_fr] + params: [ + index_name: :algoliax_people_replicas_fr, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res2 @@ -370,7 +401,13 @@ defmodule AlgoliaxTest.ReplicaTest do {:ok, %Algoliax.Response{ response: %{"objectID" => "known"}, - params: [index_name: :algoliax_people_replicas_en, object_id: "known"] + params: [ + index_name: :algoliax_people_replicas_en, + object_id: "known", + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res @@ -381,7 +418,13 @@ defmodule AlgoliaxTest.ReplicaTest do {:ok, %Algoliax.Response{ response: %{"objectID" => "known"}, - params: [index_name: :algoliax_people_replicas_fr, object_id: "known"] + params: [ + index_name: :algoliax_people_replicas_fr, + object_id: "known", + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res2 @@ -486,7 +529,12 @@ defmodule AlgoliaxTest.ReplicaTest do {:ok, %Algoliax.Response{ response: %{"searchableAttributes" => ["test"]}, - params: [index_name: :algoliax_people_replicas_en] + params: [ + index_name: :algoliax_people_replicas_en, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res @@ -497,7 +545,12 @@ defmodule AlgoliaxTest.ReplicaTest do {:ok, %Algoliax.Response{ response: %{"searchableAttributes" => ["test"]}, - params: [index_name: :algoliax_people_replicas_fr] + params: [ + index_name: :algoliax_people_replicas_fr, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res2 diff --git a/test/algoliax/routes_test.exs b/test/algoliax/routes_test.exs index 839f15e..5edfa1c 100644 --- a/test/algoliax/routes_test.exs +++ b/test/algoliax/routes_test.exs @@ -4,10 +4,9 @@ defmodule Algoliax.RoutesTest do alias Algoliax.Routes @index_name :algolia_index + @application_id "APPLICATION_ID" setup do - Application.put_env(:algoliax, :application_id, "APPLICATION_ID") - people = %{objectID: 10} {:ok, %{people: people}} @@ -15,84 +14,127 @@ defmodule Algoliax.RoutesTest do describe "First attempts" do test "url delete_index" do - assert Routes.url(:delete_index, index_name: @index_name) == + assert Routes.url(:delete_index, application_id: @application_id, index_name: @index_name) == {:delete, "http://localhost:8002/APPLICATION_ID/write/algolia_index"} end test "url get_settings" do - assert Routes.url(:get_settings, index_name: @index_name) == + assert Routes.url(:get_settings, application_id: @application_id, index_name: @index_name) == {:get, "http://localhost:8002/APPLICATION_ID/read/algolia_index/settings"} end test "url configure_index" do - assert Routes.url(:configure_index, index_name: @index_name) == + assert Routes.url(:configure_index, + application_id: @application_id, + index_name: @index_name + ) == {:put, "http://localhost:8002/APPLICATION_ID/write/algolia_index/settings"} end test "url save_objects" do - assert Routes.url(:save_objects, index_name: @index_name) == + assert Routes.url(:save_objects, application_id: @application_id, index_name: @index_name) == {:post, "http://localhost:8002/APPLICATION_ID/write/algolia_index/batch"} end test "url get_object" do - assert Routes.url(:get_object, index_name: @index_name, object_id: 10) == + assert Routes.url(:get_object, + application_id: @application_id, + index_name: @index_name, + object_id: 10 + ) == {:get, "http://localhost:8002/APPLICATION_ID/read/algolia_index/10"} end test "url save_object" do - assert Routes.url(:save_object, index_name: @index_name, object_id: 10) == + assert Routes.url(:save_object, + application_id: @application_id, + index_name: @index_name, + object_id: 10 + ) == {:put, "http://localhost:8002/APPLICATION_ID/write/algolia_index/10"} end test "url delete_object" do - assert Routes.url(:delete_object, index_name: @index_name, object_id: 10) == + assert Routes.url(:delete_object, + application_id: @application_id, + index_name: @index_name, + object_id: 10 + ) == {:delete, "http://localhost:8002/APPLICATION_ID/write/algolia_index/10"} end test "url delete_by" do - assert Routes.url(:delete_by, index_name: @index_name) == + assert Routes.url(:delete_by, application_id: @application_id, index_name: @index_name) == {:post, "http://localhost:8002/APPLICATION_ID/write/algolia_index/deleteByQuery"} end end describe "First retry" do test "url delete_index" do - assert Routes.url(:delete_index, [index_name: @index_name], 1) == + assert Routes.url( + :delete_index, + [application_id: @application_id, index_name: @index_name], + 1 + ) == {:delete, "http://localhost:8002/APPLICATION_ID/retry/1/algolia_index"} end test "url get_settings" do - assert Routes.url(:get_settings, [index_name: @index_name], 1) == + assert Routes.url( + :get_settings, + [application_id: @application_id, index_name: @index_name], + 1 + ) == {:get, "http://localhost:8002/APPLICATION_ID/retry/1/algolia_index/settings"} end test "url configure_index" do - assert Routes.url(:configure_index, [index_name: @index_name], 1) == + assert Routes.url( + :configure_index, + [application_id: @application_id, index_name: @index_name], + 1 + ) == {:put, "http://localhost:8002/APPLICATION_ID/retry/1/algolia_index/settings"} end test "url save_objects" do - assert Routes.url(:save_objects, [index_name: @index_name], 1) == + assert Routes.url( + :save_objects, + [application_id: @application_id, index_name: @index_name], + 1 + ) == {:post, "http://localhost:8002/APPLICATION_ID/retry/1/algolia_index/batch"} end test "url get_object" do - assert Routes.url(:get_object, [index_name: @index_name, object_id: 10], 1) == + assert Routes.url( + :get_object, + [application_id: @application_id, index_name: @index_name, object_id: 10], + 1 + ) == {:get, "http://localhost:8002/APPLICATION_ID/retry/1/algolia_index/10"} end test "url save_object" do - assert Routes.url(:save_object, [index_name: @index_name, object_id: 10], 1) == + assert Routes.url( + :save_object, + [application_id: @application_id, index_name: @index_name, object_id: 10], + 1 + ) == {:put, "http://localhost:8002/APPLICATION_ID/retry/1/algolia_index/10"} end test "url delete_object" do - assert Routes.url(:delete_object, [index_name: @index_name, object_id: 10], 1) == + assert Routes.url( + :delete_object, + [application_id: @application_id, index_name: @index_name, object_id: 10], + 1 + ) == {:delete, "http://localhost:8002/APPLICATION_ID/retry/1/algolia_index/10"} end test "url delete_by" do - assert Routes.url(:delete_by, [index_name: @index_name], 1) == + assert Routes.url(:delete_by, [application_id: @application_id, index_name: @index_name], 1) == {:post, "http://localhost:8002/APPLICATION_ID/retry/1/algolia_index/deleteByQuery"} end end diff --git a/test/algoliax/struct_test.exs b/test/algoliax/struct_test.exs index 4a7e975..e69398a 100644 --- a/test/algoliax/struct_test.exs +++ b/test/algoliax/struct_test.exs @@ -4,10 +4,14 @@ defmodule AlgoliaxTest.StructTest do alias Algoliax.Schemas.{ PeopleStruct, PeopleStructMultipleIndexes, - PeopleStructRuntimeMultipleIndexes, - PeopleStructRuntimeIndexName + PeopleStructCredentials, + PeopleStructRuntimeIndexName, + PeopleStructRuntimeMultipleIndexes } + @application_id "APPLICATION_ID" + @api_key "api_key" + setup do Algoliax.SettingsStore.set_settings(:algoliax_people_struct, %{}) Algoliax.SettingsStore.set_settings(:algoliax_people_with_prepare_object_struct, %{}) @@ -171,7 +175,12 @@ defmodule AlgoliaxTest.StructTest do {:ok, %Algoliax.Response{ response: %{"taskID" => _, "updatedAt" => _}, - params: [index_name: :algoliax_people_struct_en] + params: [ + index_name: :algoliax_people_struct_en, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res @@ -182,7 +191,12 @@ defmodule AlgoliaxTest.StructTest do {:ok, %Algoliax.Response{ response: %{"taskID" => _, "updatedAt" => _}, - params: [index_name: :algoliax_people_struct_fr] + params: [ + index_name: :algoliax_people_struct_fr, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res2 @@ -222,7 +236,13 @@ defmodule AlgoliaxTest.StructTest do {:ok, %Algoliax.Response{ response: %{"objectID" => ^reference, "taskID" => _, "updatedAt" => _}, - params: [index_name: :algoliax_people_struct_en, object_id: ^reference] + params: [ + index_name: :algoliax_people_struct_en, + object_id: ^reference, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res @@ -233,7 +253,13 @@ defmodule AlgoliaxTest.StructTest do {:ok, %Algoliax.Response{ response: %{"objectID" => ^reference, "taskID" => _, "updatedAt" => _}, - params: [index_name: :algoliax_people_struct_fr, object_id: ^reference] + params: [ + index_name: :algoliax_people_struct_fr, + object_id: ^reference, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res2 @@ -292,7 +318,12 @@ defmodule AlgoliaxTest.StructTest do {:ok, %Algoliax.Response{ response: %{"taskID" => _, "objectIDs" => [^reference1]}, - params: [index_name: :algoliax_people_struct_en] + params: [ + index_name: :algoliax_people_struct_en, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res @@ -303,7 +334,12 @@ defmodule AlgoliaxTest.StructTest do {:ok, %Algoliax.Response{ response: %{"taskID" => _, "objectIDs" => [^reference1]}, - params: [index_name: :algoliax_people_struct_fr] + params: [ + index_name: :algoliax_people_struct_fr, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res2 @@ -351,7 +387,12 @@ defmodule AlgoliaxTest.StructTest do {:ok, %Algoliax.Response{ response: %{"taskID" => _, "objectIDs" => [^reference1, ^reference2]}, - params: [index_name: :algoliax_people_struct_en] + params: [ + index_name: :algoliax_people_struct_en, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res @@ -362,7 +403,12 @@ defmodule AlgoliaxTest.StructTest do {:ok, %Algoliax.Response{ response: %{"taskID" => _, "objectIDs" => [^reference1, ^reference2]}, - params: [index_name: :algoliax_people_struct_fr] + params: [ + index_name: :algoliax_people_struct_fr, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res2 @@ -404,7 +450,13 @@ defmodule AlgoliaxTest.StructTest do {:ok, %Algoliax.Response{ response: %{"objectID" => "known"}, - params: [index_name: :algoliax_people_struct_en, object_id: "known"] + params: [ + index_name: :algoliax_people_struct_en, + object_id: "known", + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res @@ -415,7 +467,13 @@ defmodule AlgoliaxTest.StructTest do {:ok, %Algoliax.Response{ response: %{"objectID" => "known"}, - params: [index_name: :algoliax_people_struct_fr, object_id: "known"] + params: [ + index_name: :algoliax_people_struct_fr, + object_id: "known", + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res2 @@ -487,7 +545,12 @@ defmodule AlgoliaxTest.StructTest do {:ok, %Algoliax.Response{ response: %{"searchableAttributes" => ["test"]}, - params: [index_name: :algoliax_people_struct_en] + params: [ + index_name: :algoliax_people_struct_en, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res @@ -498,7 +561,12 @@ defmodule AlgoliaxTest.StructTest do {:ok, %Algoliax.Response{ response: %{"searchableAttributes" => ["test"]}, - params: [index_name: :algoliax_people_struct_fr] + params: [ + index_name: :algoliax_people_struct_fr, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res2 @@ -551,7 +619,12 @@ defmodule AlgoliaxTest.StructTest do {:ok, %Algoliax.Response{ response: %{"taskID" => _, "updatedAt" => _}, - params: [index_name: :algoliax_people_struct_en] + params: [ + index_name: :algoliax_people_struct_en, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res @@ -562,7 +635,12 @@ defmodule AlgoliaxTest.StructTest do {:ok, %Algoliax.Response{ response: %{"taskID" => _, "updatedAt" => _}, - params: [index_name: :algoliax_people_struct_fr] + params: [ + index_name: :algoliax_people_struct_fr, + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res2 @@ -613,7 +691,13 @@ defmodule AlgoliaxTest.StructTest do {:ok, %Algoliax.Response{ response: %{"objectID" => "known"}, - params: [index_name: :people_runtime_index_name_en, object_id: "known"] + params: [ + index_name: :people_runtime_index_name_en, + object_id: "known", + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res @@ -624,7 +708,13 @@ defmodule AlgoliaxTest.StructTest do {:ok, %Algoliax.Response{ response: %{"objectID" => "known"}, - params: [index_name: :people_runtime_index_name_fr, object_id: "known"] + params: [ + index_name: :people_runtime_index_name_fr, + object_id: "known", + application_id: @application_id + ], + application_id: @application_id, + api_key: @api_key }} ] } = res2 @@ -638,6 +728,67 @@ defmodule AlgoliaxTest.StructTest do end end + describe "configured credentials" do + test "get_object/1" do + person = %PeopleStructCredentials{ + reference: "known", + last_name: "Doe", + first_name: "John", + age: 77 + } + + assert {:ok, res} = PeopleStructCredentials.get_object(person) + + assert %Algoliax.Response{ + response: %{"objectID" => "known"}, + params: [ + index_name: :people_runtime_index, + object_id: "known", + application_id: "APPLICATION_ID_1" + ], + application_id: "APPLICATION_ID_1", + api_key: "api_key_1" + } == + res + + assert_request( + "GET", + %{ + path: "/APPLICATION_ID_1/read/people_runtime_index/known", + body: %{}, + headers: [ + {"x-algolia-api-key", "api_key_1"}, + {"x-algolia-application-id", "APPLICATION_ID_1"} + ] + } + ) + + assert_request( + "GET", + %{ + path: "/APPLICATION_ID_1/read/people_runtime_index/settings", + body: %{}, + headers: [ + {"x-algolia-api-key", "api_key_1"}, + {"x-algolia-application-id", "APPLICATION_ID_1"} + ] + } + ) + + assert_request( + "PUT", + %{ + path: "/APPLICATION_ID_1/write/people_runtime_index/settings", + body: %{}, + headers: [ + {"x-algolia-api-key", "api_key_1"}, + {"x-algolia-application-id", "APPLICATION_ID_1"} + ] + } + ) + end + end + describe "wait for task" do reference = :rand.uniform(1_000_000) |> to_string() person = %PeopleStruct{reference: reference, last_name: "Doe", first_name: "John", age: 77} @@ -669,12 +820,20 @@ defmodule AlgoliaxTest.StructTest do assert %Algoliax.Response{ response: %{"objectID" => ^reference, "taskID" => task_id, "updatedAt" => _}, - params: [index_name: :algoliax_people_struct_en, object_id: ^reference] + params: [ + index_name: :algoliax_people_struct_en, + object_id: ^reference, + application_id: "APPLICATION_ID" + ] } = res assert %Algoliax.Response{ response: %{"objectID" => ^reference, "taskID" => task_id2, "updatedAt" => _}, - params: [index_name: :algoliax_people_struct_fr, object_id: ^reference] + params: [ + index_name: :algoliax_people_struct_fr, + object_id: ^reference, + application_id: "APPLICATION_ID" + ] } = res2 # Assert that there are 4 calls to check task status per index diff --git a/test/algoliax/utils_test.exs b/test/algoliax/utils_test.exs index 134b584..cc4d266 100644 --- a/test/algoliax/utils_test.exs +++ b/test/algoliax/utils_test.exs @@ -150,6 +150,42 @@ defmodule Algoliax.UtilsTest do end end + describe "api_key/1" do + test "with a function" do + assert Algoliax.Utils.api_key(credentials: :custom_1) == + "api_key_1" + end + + test "without a function (uses Algoliax.Config)" do + assert Algoliax.Utils.api_key([]) == + "api_key" + end + + test "missing config" do + assert_raise(Algoliax.InvalidAlgoliaCredentialsError, fn -> + Algoliax.Utils.api_key(credentials: :custom_unknown) + end) + end + end + + describe "application_id/1" do + test "with a config" do + assert Algoliax.Utils.application_id(credentials: :custom_1) == + "APPLICATION_ID_1" + end + + test "without a config (uses Algoliax.Config)" do + assert Algoliax.Utils.application_id([]) == + "APPLICATION_ID" + end + + test "missing config" do + assert_raise(Algoliax.InvalidAlgoliaCredentialsError, fn -> + Algoliax.Utils.application_id(credentials: :custom_unknown) + end) + end + end + describe "index_name/2" do test "with a function" do assert Algoliax.Utils.index_name(IndexNameFromFunction, index_name: :algoliax_people) == diff --git a/test/support/schemas/people_struct_runtime_credentials.ex b/test/support/schemas/people_struct_runtime_credentials.ex new file mode 100644 index 0000000..fd96b5b --- /dev/null +++ b/test/support/schemas/people_struct_runtime_credentials.ex @@ -0,0 +1,15 @@ +defmodule Algoliax.Schemas.PeopleStructCredentials do + @moduledoc false + + use Algoliax.Indexer, + index_name: :people_runtime_index, + object_id: :reference, + algolia: [ + attributes_for_faceting: ["age"], + searchable_attributes: ["full_name"], + custom_ranking: ["desc(update_at)"] + ], + credentials: :custom_1 + + defstruct reference: nil, last_name: nil, first_name: nil, age: nil +end