From 1ec08acf84ed239c4513e2e81ca9f44537848d6a Mon Sep 17 00:00:00 2001 From: jaeyson Date: Tue, 21 May 2024 14:19:23 +0800 Subject: [PATCH 01/34] Configuring a connection string dynamically --- .github/workflows/ci.yml | 38 ++++--- CHANGELOG.md | 52 +++++++-- README.md | 26 ++++- docker-compose.yml | 17 --- guides/running_local_typesense.md | 25 ++++- lib/ex_typesense.ex | 40 ++++--- lib/ex_typesense/cluster.ex | 27 +++-- lib/ex_typesense/collection.ex | 178 +++++++++++++++--------------- lib/ex_typesense/connection.ex | 19 ++++ lib/ex_typesense/http_client.ex | 62 ++++++++++- linux.yml | 10 ++ mix.exs | 6 +- mix.lock | 16 +-- osx.yml | 10 ++ test/cluster_test.exs | 31 +++--- test/collection_test.exs | 91 ++++++++------- 16 files changed, 412 insertions(+), 236 deletions(-) delete mode 100644 docker-compose.yml create mode 100644 lib/ex_typesense/connection.ex create mode 100644 linux.yml create mode 100644 osx.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d371b4f..b096a5a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,40 +7,43 @@ on: branches: ["main"] jobs: - check-commit-message: - name: Check commit message for skipping build(s) - runs-on: ubuntu-latest - # skips CI and prints cli message, without fail build badge - if: contains(github.event.head_commit.message, '[skip ci]') - steps: - - name: print message via cli - run: echo "no need to build, based from commit message" + # https://docs.github.com/en/actions/managing-workflow-runs/skipping-workflow-runs + # Workflows that would otherwise be triggered using `on: push` or + # `on: pull_request` won't be triggered if you add any of the + # following strings to the commit message in a push, or the HEAD + # commit of a pull request: + # - [skip ci] + # - [ci skip] + # - [no ci] + # - [skip actions] + # - [actions skip] test: runs-on: ubuntu-latest strategy: matrix: - typesense-version: [0.25.1] + typesense-version: [0.25.2 26.0] env: MIX_ENV: test + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: erlef/setup-beam@v1 with: otp-version: '25' elixir-version: '1.14.x' - - uses: actions/cache@v2 - id: mix-cache + - uses: actions/cache@v4 with: path: | deps - key: ${{ runner.os }}-mix-${{ hashFiles('mix.lock') }} + _build + key: ${{ runner.os }}-mixtest-${{ hashFiles('**/mix.lock') }} restore-keys: | - ${{ runner.os }}-mix- + ${{ runner.os }}-mixtest- - name: Start Typesense uses: jirevwe/typesense-github-action@v1.0.1 @@ -50,8 +53,8 @@ jobs: - name: Install Dependencies run: | - mix local.rebar --force - mix local.hex --force + mix local.rebar --if-missing + mix local.hex --if-missing mix deps.get mix format --check-formatted @@ -63,3 +66,6 @@ jobs: - name: Run tests run: mix test + + - name: Post test coverage to Coveralls + run: mix coveralls.github diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c8a05b..e83945b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,45 +5,83 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.4.0 (2024.05.20) + +### Added + +* Connection module for dynamic loading of credentials. +* Default connection config when running commands (e.g. create collections, etc.). + +### Changed + +* Refactor `ExTypesense.HttpClient` on how to handle request. +* Bumped dependencies' version. + +### Deprecated + +* Some functions from `Cluster`, `Collection`, `Document`, `HttpClient` and `Search` where soft depcrated in order to incorporate the `Connection` module for dynamic connections loaded from Ecto schema. If you read the docs, you might notice it's marked with `deprecated` and encourages to use the newer ones. + ## 0.3.5 (2023.08.13) +### Fixed + * Fixed typos ## 0.3.4 (2023.07.12) +### Changed + * Remove string conversion on struct id when deleting a document. ## 0.3.3 (2023.07.11) -* Add index_multiple_documents/1 clause for accepting struct args +### Added + +* Add index_multiple_documents/1 clause for accepting struct args. ## 0.3.2 (2023.07.11) -* Maps struct pk to document's id -* Update http request timeout to `3,600` seconds +### Changed + +* Maps struct pk to document's id. +* Update http request timeout to `3,600` seconds. ## 0.3.1 (2023.07.11) -* Increase connection timeout +### Changed + +* Increase connection timeout. ## 0.3.0 (2023.06.20) -* Fixed url request path for aliases +### Fixed + +* Fixed url request path for aliases. + +### Changed * Refactor functions inside collection and document. * Changed return values from `{:ok, t()}` to `t()` only. -* Added cheatsheet section on docs * Parse schema field types for `float`, `boolean`, `string`, `integer` and a list with these corresponding types. +### Added +* Added cheatsheet section on docs. + ## 0.2.2 (2023.01.26) +### Changed + * Updated docs ## 0.2.1 (2023.01.22) -* Returned an ecto query instead of list of results +### Changed + +* Returned an ecto query instead of list of results. ## 0.2.0 (2023.01.20) +### Added + * Added search function which returns a list of structs or empty. ## 0.1.0 (2023.01.20) diff --git a/README.md b/README.md index bc29be3..508d1b8 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,12 @@ [![Hex.pm](https://img.shields.io/hexpm/v/ex_typesense)](https://hex.pm/packages/ex_typesense) [![Hexdocs.pm](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/ex_typesense) [![Hex.pm](https://img.shields.io/hexpm/l/ex_typesense)](LICENSE) -[![Typesense badge](https://img.shields.io/badge/Typesense-v0.25.2-darkblue)](https://typesense.org/docs/0.25.2/api) +[![Typesense badge](https://img.shields.io/badge/Typesense-v26.0-darkblue)](https://typesense.org/docs/26.0/api) Typesense client for Elixir with support for your Ecto schemas. +**Note**: Breaking changes if you're upgrading from `0.3.x` to upcoming `0.5.x` version. + ## Todo - creating collection using auto schema detection @@ -25,7 +27,7 @@ Add `:ex_typesense` to your list of dependencies in the Elixir project config fi def deps do [ # From default Hex package manager - {:ex_typesense, "~> 0.3"} + {:ex_typesense, "~> 0.4"} # Or from GitHub repository, if you want to latest greatest from main branch {:ex_typesense, git: "https://github.com/jaeyson/ex_typesense.git"} @@ -39,7 +41,19 @@ end After you have setup a [local](./guides/running_local_typesense.md) Typesense or [Cloud hosted](https://cloud.typesense.org) instance, you can set the following config details to the config file: -For local instance: +#### Run local Typesense instance + +```bash +# Linux +docker compose -f linux.yml up -d + +# Mac OS, the difference is using arm64 arch +docker compose -f osx.yml up -d +``` + +More info on spinning a local instance: https://typesense.org/docs/guide/install-typesense + +#### Set credentials via config (e.g. `config/runtime.exs`) ```elixir config :ex_typesense, @@ -61,7 +75,9 @@ config :ex_typesense, ### 2. Create a collection -#### Using Ecto +There are 2 ways to create a collection, either via [Ecto schema](https://hexdocs.pm/ecto/Ecto.Schema.html) or using map ([an Elixir data type](https://hexdocs.pm/elixir/keywords-and-maps.html#maps-as-key-value-pairs)): + +#### Option 1: using Ecto In this example, we're adding `person_id` that points to the id of `persons` schema. @@ -110,7 +126,7 @@ Next, create the collection from a module name. ExTypesense.create_collection(Person) ``` -#### Using Maps +#### Option 2: using map ```elixir schema = %{ diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index baf1f9b..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -version: "3" -services: - typesense: - image: docker.io/typesense/typesense:0.25.2 - container_name: typesense - tty: true - restart: on-failure - environment: - - TYPESENSE_DATA_DIR=/data - - TYPESENSE_API_KEY=xyz - volumes: - - ./typesense-data:/data - ports: - # for internal status of the typesense server - - 8107:8107 - # for actual typesense server - - 8108:8108 diff --git a/guides/running_local_typesense.md b/guides/running_local_typesense.md index 46122dc..ad8639c 100644 --- a/guides/running_local_typesense.md +++ b/guides/running_local_typesense.md @@ -16,12 +16,18 @@ machine: - Docker Compose: - Docker Compose helps manage multi-container applications like Typesense. +Or use the [one line command](https://github.com/docker/docker-install) to install both: + +```bash +# this is slightly edited so that you don't +# have to save the file on the machine +curl -sSL https://get.docker.com/ | sh +``` ## Setting up We are using Docker compose to bootstrap a local Typesense instance from a -sample `docker-compose.yml` file. - +sample docker compose file. Clone the `ex_typesense` GitHub repository: @@ -33,9 +39,16 @@ Navigate to the cloned GitHub repository start the Typesense instance: ```bash cd ex_typesense -docker-compose up -d + +# using Linux +docker compose -f linux.yml up -d + +# or using Mac OS, the difference is using arm64 arch +docker compose -f osx.yml up -d ``` +More info on spinning a local instance: https://typesense.org/docs/guide/install-typesense + Once you've started Typesense, you can verify its installation by accessing the health endpoint through a browser or `curl` in the terminal: @@ -48,5 +61,9 @@ In a separate terminal, you can view the logs of your local Typesense instance using following command: ```bash -docker-compose logs -f +# using Linux +docker compose -f linux.yml logs -f + +# or using Mac OS +docker compose -f osx.yml logs -f ``` diff --git a/lib/ex_typesense.ex b/lib/ex_typesense.ex index 8d5bec3..310ffb7 100644 --- a/lib/ex_typesense.ex +++ b/lib/ex_typesense.ex @@ -48,21 +48,35 @@ defmodule ExTypesense do ``` """ + alias ExTypesense.Connection + @callback get_field_types :: any() # collection-specific tasks - defdelegate list_collections, to: ExTypesense.Collection - defdelegate create_collection(schema), to: ExTypesense.Collection - defdelegate get_collection(name), to: ExTypesense.Collection - defdelegate get_collection_name(alias_name), to: ExTypesense.Collection - defdelegate drop_collection(collection_name), to: ExTypesense.Collection - defdelegate update_collection_fields(collection_name, fields), to: ExTypesense.Collection + defdelegate list_collections(conn \\ Connection.new()), to: ExTypesense.Collection + defdelegate create_collection(conn \\ Connection.new(), schema), to: ExTypesense.Collection + defdelegate get_collection(conn \\ Connection.new(), name), to: ExTypesense.Collection + + defdelegate get_collection_name(conn \\ Connection.new(), alias_name), + to: ExTypesense.Collection + + defdelegate drop_collection(conn \\ Connection.new(), collection_name), + to: ExTypesense.Collection + + defdelegate update_collection_fields(conn \\ Connection.new(), collection_name, fields), + to: ExTypesense.Collection # collection alias - defdelegate list_collection_aliases, to: ExTypesense.Collection - defdelegate get_collection_alias(alias_name), to: ExTypesense.Collection - defdelegate delete_collection_alias(alias_name), to: ExTypesense.Collection - defdelegate upsert_collection_alias(alias_name, collection_name), to: ExTypesense.Collection + defdelegate list_collection_aliases(conn \\ Connection.new()), to: ExTypesense.Collection + + defdelegate get_collection_alias(conn \\ Connection.new(), alias_name), + to: ExTypesense.Collection + + defdelegate delete_collection_alias(conn \\ Connection.new(), alias_name), + to: ExTypesense.Collection + + defdelegate upsert_collection_alias(conn \\ Connection.new(), alias_name, collection_name), + to: ExTypesense.Collection # document-specific tasks defdelegate get_document(collection_name, document_id), to: ExTypesense.Document @@ -87,7 +101,7 @@ defmodule ExTypesense do # synonyms # cluster operations - defdelegate api_stats, to: ExTypesense.Cluster - defdelegate cluster_metrics, to: ExTypesense.Cluster - defdelegate health, to: ExTypesense.Cluster + defdelegate api_stats(conn \\ Connection.new()), to: ExTypesense.Cluster + defdelegate cluster_metrics(conn \\ Connection.new()), to: ExTypesense.Cluster + defdelegate health(conn \\ Connection.new()), to: ExTypesense.Cluster end diff --git a/lib/ex_typesense/cluster.ex b/lib/ex_typesense/cluster.ex index 43c10cc..8aea329 100644 --- a/lib/ex_typesense/cluster.ex +++ b/lib/ex_typesense/cluster.ex @@ -1,18 +1,21 @@ defmodule ExTypesense.Cluster do - alias ExTypesense.HttpClient - + @moduledoc since: "0.3.0" @moduledoc """ Cluster specific operations. """ + alias ExTypesense.Connection + alias ExTypesense.HttpClient + @type response() :: any() | {:ok, any()} | {:error, map()} @doc """ Get health information about a Typesense node. """ - @spec health :: response() - def health do - case HttpClient.run(:get, "/health") do + @doc since: "0.3.0" + @spec health(Connection.t()) :: response() + def health(conn \\ Connection.new()) do + case HttpClient.request(conn, %{method: :get, path: "/health"}) do {:ok, %{"ok" => true}} -> {:ok, true} @@ -26,16 +29,18 @@ defmodule ExTypesense.Cluster do This endpoint returns average requests per second and latencies for all requests in the last 10 seconds. """ - @spec api_stats :: response() - def api_stats do - HttpClient.run(:get, "/stats.json") + @doc since: "0.3.0" + @spec api_stats(Connection.t()) :: response() + def api_stats(connection \\ Connection.new()) do + HttpClient.request(connection, %{method: :get, path: "/stats.json"}) end @doc """ Get current RAM, CPU, Disk & Network usage metrics. """ - @spec cluster_metrics :: response() - def cluster_metrics do - HttpClient.run(:get, "/metrics.json") + @doc since: "0.3.0" + @spec cluster_metrics(Connection.t()) :: response() + def cluster_metrics(connection \\ Connection.new()) do + HttpClient.request(connection, %{method: :get, path: "/metrics.json"}) end end diff --git a/lib/ex_typesense/collection.ex b/lib/ex_typesense/collection.ex index 6fc7686..d1617aa 100644 --- a/lib/ex_typesense/collection.ex +++ b/lib/ex_typesense/collection.ex @@ -7,6 +7,7 @@ defmodule ExTypesense.Collection do """ alias ExTypesense.HttpClient + alias ExTypesense.Connection defmodule Field do @moduledoc since: "0.1.0" @@ -100,9 +101,9 @@ defmodule ExTypesense.Collection do Lists all collections. """ @doc since: "0.1.0" - @spec list_collections() :: response() - def list_collections do - case HttpClient.run(:get, @collections_path) do + @spec list_collections(Connection.t()) :: response() + def list_collections(conn \\ Connection.new()) do + case HttpClient.request(conn, %{method: :get, path: @collections_path}) do {:ok, collections} -> Stream.map(collections, &convert_to_struct/1) |> Enum.to_list() @@ -115,10 +116,10 @@ defmodule ExTypesense.Collection do Get the collection name by alias. """ @doc since: "0.3.0" - @spec get_collection_name(String.t() | module()) :: String.t() - def get_collection_name(alias_name) do - alias_name - |> get_collection_alias() + @spec get_collection_name(Connection.t(), String.t() | module()) :: String.t() + def get_collection_name(conn \\ Connection.new(), alias_name) do + conn + |> get_collection_alias(alias_name) |> Map.get("collection_name") end @@ -126,23 +127,22 @@ defmodule ExTypesense.Collection do Get a specific collection by string or module name. """ @doc since: "0.1.0" - @spec get_collection(String.t() | module()) :: response() - def get_collection(name) when is_atom(name) do - collection_name = name.__schema__(:source) + @spec get_collection(Connection.t(), String.t() | module()) :: response() + def get_collection(conn \\ Connection.new(), name) - [@collections_path, collection_name] - |> Path.join() - |> do_get_collection() + def get_collection(conn, name) when is_atom(name) do + collection_name = name.__schema__(:source) + path = Path.join([@collections_path, collection_name]) + do_get_collection(conn, path) end - def get_collection(name) when is_binary(name) do - [@collections_path, name] - |> Path.join() - |> do_get_collection() + def get_collection(conn, name) when is_binary(name) do + path = Path.join([@collections_path, name]) + do_get_collection(conn, path) end - defp do_get_collection(path) do - case HttpClient.run(:get, path) do + defp do_get_collection(conn, path) do + case HttpClient.request(conn, %{method: :get, path: path}) do {:ok, collection} -> convert_to_struct(collection) @@ -189,27 +189,29 @@ defmodule ExTypesense.Collection do } """ @doc since: "0.1.0" - @spec create_collection(schema :: module() | map()) :: response() - def create_collection(schema) when is_atom(schema) do + @spec create_collection(Connection.t(), schema :: module() | map()) :: response() + def create_collection(conn \\ Connection.new(), schema) + + def create_collection(conn, schema) when is_atom(schema) do schema = schema.get_field_types() |> Map.put(:name, schema.__schema__(:source)) - do_create_collection(schema) + do_create_collection(conn, schema) end - def create_collection(schema) when is_map(schema) do + def create_collection(conn, schema) when is_map(schema) do schema = Map.put(schema, :name, schema[:name]) - do_create_collection(schema) + do_create_collection(conn, schema) end - def create_collection(_schema), do: {:error, "wrong argument(s) passed"} + def create_collection(_conn, _schema), do: {:error, "wrong argument(s) passed"} - defp do_create_collection(schema) do + defp do_create_collection(conn, schema) do body = Jason.encode!(schema) - case HttpClient.run(:post, @collections_path, body) do + case HttpClient.request(conn, %{method: :post, path: @collections_path, body: body}) do {:ok, collection} -> convert_to_struct(collection) @@ -258,27 +260,26 @@ defmodule ExTypesense.Collection do } """ @doc since: "0.1.0" - @spec update_collection_fields(name :: String.t() | module(), map()) :: response() - def update_collection_fields(name, fields \\ %{}) + @spec update_collection_fields(Connection.t(), name :: String.t() | module(), map()) :: + response() + def update_collection_fields(conn \\ Connection.new(), name, fields \\ %{}) - def update_collection_fields(name, fields) when is_atom(name) do + def update_collection_fields(conn, name, fields) when is_atom(name) do collection_name = name.__schema__(:source) - [@collections_path, collection_name] - |> Path.join() - |> do_update_collection_fields(Jason.encode!(fields)) + path = Path.join([@collections_path, collection_name]) + do_update_collection_fields(conn, path, Jason.encode!(fields)) end - def update_collection_fields(name, fields) when is_binary(name) do - [@collections_path, name] - |> Path.join() - |> do_update_collection_fields(Jason.encode!(fields)) + def update_collection_fields(conn, name, fields) when is_binary(name) do + path = Path.join([@collections_path, name]) + do_update_collection_fields(conn, path, Jason.encode!(fields)) end - def update_collection_fields(_name, _schema), do: {:error, "wrong argument(s) passed"} + def update_collection_fields(_conn, _name, _schema), do: {:error, "wrong argument(s) passed"} - defp do_update_collection_fields(path, body) do - case HttpClient.run(:patch, path, body) do + defp do_update_collection_fields(conn, path, body) do + case HttpClient.request(conn, %{method: :patch, path: path, body: body}) do {:ok, collection} -> convert_to_struct(collection) @@ -293,23 +294,23 @@ defmodule ExTypesense.Collection do **Note**: dropping a collection does not remove the referenced alias, only the indexed documents. """ @doc since: "0.1.0" - @spec drop_collection(name :: String.t() | module()) :: response() - def drop_collection(name) when is_atom(name) do + @spec drop_collection(Connection.t(), name :: String.t() | module()) :: response() + def drop_collection(conn \\ Connection.new(), name) + + def drop_collection(conn, name) when is_atom(name) do collection_name = name.__schema__(:source) - [@collections_path, collection_name] - |> Path.join() - |> do_drop_collection() + path = Path.join([@collections_path, collection_name]) + do_drop_collection(conn, path) end - def drop_collection(name) when is_binary(name) do - [@collections_path, name] - |> Path.join() - |> do_drop_collection() + def drop_collection(conn, name) when is_binary(name) do + path = Path.join([@collections_path, name]) + do_drop_collection(conn, path) end - defp do_drop_collection(path) do - case HttpClient.run(:delete, path) do + defp do_drop_collection(conn, path) do + case HttpClient.request(conn, %{method: :delete, path: path}) do {:ok, collection} -> convert_to_struct(collection) @@ -322,9 +323,9 @@ defmodule ExTypesense.Collection do List all aliases and the corresponding collections that they map to. """ @doc since: "0.1.0" - @spec list_collection_aliases() :: response() - def list_collection_aliases do - case HttpClient.run(:get, @aliases_path) do + @spec list_collection_aliases(Connection.t()) :: response() + def list_collection_aliases(conn \\ Connection.new()) do + case HttpClient.request(conn, %{method: :get, path: @aliases_path}) do {:ok, %{"aliases" => aliases}} -> aliases @@ -337,24 +338,24 @@ defmodule ExTypesense.Collection do Get a specific collection alias by string or module name. """ @doc since: "0.1.0" - @spec get_collection_alias(String.t() | module()) :: response() - def get_collection_alias(alias_name) when is_atom(alias_name) do - [@aliases_path, alias_name.__schema__(:source)] - |> Path.join() - |> do_get_collection_alias() + @spec get_collection_alias(Connection.t(), String.t() | module()) :: response() + def get_collection_alias(conn \\ Connection.new(), alias_name) + + def get_collection_alias(conn, alias_name) when is_atom(alias_name) do + path = Path.join([@aliases_path, alias_name.__schema__(:source)]) + do_get_collection_alias(conn, path) end - def get_collection_alias(alias_name) when is_binary(alias_name) do - [@aliases_path, alias_name] - |> Path.join() - |> do_get_collection_alias() + def get_collection_alias(conn, alias_name) when is_binary(alias_name) do + path = Path.join([@aliases_path, alias_name]) + do_get_collection_alias(conn, path) end - def get_collection_alias(_alias_name), do: {:error, "wrong argument(s) passed"} + def get_collection_alias(_conn, _alias_name), do: {:error, "wrong argument(s) passed"} - @spec do_get_collection_alias(String.t()) :: response() - defp do_get_collection_alias(path) do - case HttpClient.run(:get, path) do + # @spec do_get_collection_alias(String.t()) :: response() + defp do_get_collection_alias(conn, path) do + case HttpClient.request(conn, %{method: :get, path: path}) do {:ok, collection_alias} -> collection_alias @@ -367,23 +368,26 @@ defmodule ExTypesense.Collection do Upserts a collection alias. """ @doc since: "0.1.0" - @spec upsert_collection_alias(String.t() | module(), String.t()) :: response() - def upsert_collection_alias(alias_name, collection_name) when is_atom(alias_name) do + @spec upsert_collection_alias(Connection.t(), String.t() | module(), String.t()) :: response() + def upsert_collection_alias(conn \\ Connection.new(), alias_name, collection_name) + + def upsert_collection_alias(conn, alias_name, collection_name) when is_atom(alias_name) do path = Path.join([@aliases_path, alias_name.__schema__(:source)]) body = Jason.encode!(%{collection_name: collection_name}) - do_upsert_collection_alias(path, body) + do_upsert_collection_alias(conn, path, body) end - def upsert_collection_alias(alias_name, collection_name) when is_binary(alias_name) do + def upsert_collection_alias(conn, alias_name, collection_name) when is_binary(alias_name) do path = Path.join([@aliases_path, alias_name]) body = Jason.encode!(%{collection_name: collection_name}) - do_upsert_collection_alias(path, body) + do_upsert_collection_alias(conn, path, body) end - def upsert_collection_alias(_alias_name), do: {:error, "wrong argument(s) passed"} + def upsert_collection_alias(_conn, _alias_name, _collection_name), + do: {:error, "wrong argument(s) passed"} - defp do_upsert_collection_alias(path, body) do - case HttpClient.run(:put, path, body) do + defp do_upsert_collection_alias(conn, path, body) do + case HttpClient.request(conn, %{method: :put, path: path, body: body}) do {:ok, collection_alias} -> collection_alias @@ -397,23 +401,23 @@ defmodule ExTypesense.Collection do is not affected by this action. """ @doc since: "0.1.0" - @spec delete_collection_alias(String.t() | module()) :: response() - def delete_collection_alias(alias_name) when is_atom(alias_name) do - [@aliases_path, alias_name.__schema__(:source)] - |> Path.join() - |> do_delete_collection_alias() + @spec delete_collection_alias(Connection.t(), String.t() | module()) :: response() + def delete_collection_alias(conn \\ Connection.new(), alias_name) + + def delete_collection_alias(conn, alias_name) when is_atom(alias_name) do + path = Path.join([@aliases_path, alias_name.__schema__(:source)]) + do_delete_collection_alias(conn, path) end - def delete_collection_alias(alias_name) when is_binary(alias_name) do - [@aliases_path, alias_name] - |> Path.join() - |> do_delete_collection_alias() + def delete_collection_alias(conn, alias_name) when is_binary(alias_name) do + path = Path.join([@aliases_path, alias_name]) + do_delete_collection_alias(conn, path) end - def delete_collection_alias(_alias_name), do: {:error, "wrong argument(s) passed"} + def delete_collection_alias(_conn, _alias_name), do: {:error, "wrong argument(s) passed"} - defp do_delete_collection_alias(path) do - case HttpClient.run(:delete, path) do + defp do_delete_collection_alias(conn, path) do + case HttpClient.request(conn, %{method: :delete, path: path}) do {:ok, collection_alias} -> collection_alias diff --git a/lib/ex_typesense/connection.ex b/lib/ex_typesense/connection.ex new file mode 100644 index 0000000..d6c5fc2 --- /dev/null +++ b/lib/ex_typesense/connection.ex @@ -0,0 +1,19 @@ +defmodule ExTypesense.Connection do + @derive {Inspect, except: [:api_key]} + defstruct host: ExTypesense.HttpClient.get_host(), + api_key: ExTypesense.HttpClient.api_key(), + port: ExTypesense.HttpClient.get_port(), + scheme: ExTypesense.HttpClient.get_scheme() + + @type t() :: %__MODULE__{} + + @spec new(connection :: struct()) :: ExTypesense.Connection.t() + def new(connection \\ %__MODULE__{}) when is_struct(connection) do + %__MODULE__{ + host: Map.get(connection, :host), + api_key: Map.get(connection, :api_key), + port: Map.get(connection, :port), + scheme: Map.get(connection, :scheme) + } + end +end diff --git a/lib/ex_typesense/http_client.ex b/lib/ex_typesense/http_client.ex index db3a2a0..9c3ae9e 100644 --- a/lib/ex_typesense/http_client.ex +++ b/lib/ex_typesense/http_client.ex @@ -4,6 +4,8 @@ defmodule ExTypesense.HttpClient do Http client for Typesense server. """ + alias ExTypesense.Connection + @type request_body() :: iodata() | nil @type request_method() :: :get | :post | :delete | :patch | :put @type request_path() :: String.t() @@ -14,6 +16,61 @@ defmodule ExTypesense.HttpClient do def get_scheme, do: Application.get_env(:ex_typesense, :scheme) def api_key, do: Application.get_env(:ex_typesense, :api_key) + @doc """ + Command for making http requests. + + ## Examples + iex> connection = %ExTypesense.Connection{ + ...> host: "localhost", + ...> api_key: "some_api_key", + ...> port: "8108", + ...> scheme: "http" + ...> } + iex> HttpClient.request(connection, %{method: :post, path: "/collections", body: ExTypesense.TestSchema.Person}) + {:ok, + [%{ + "created_at" => 123456789, + "default_sorting_field" => "person_id", + "fields" => [...], + "name" => "persons", + "num_documents" => 0, + "symbols_to_index" => [], + "token_separators" => [] + }] + } + """ + @doc since: "0.1.0" + @spec request(Connection.t(), map()) :: nil + def request(conn, opts \\ %{}) do + url = + %URI{ + scheme: conn.scheme, + host: conn.host, + port: conn.port, + path: opts[:path], + query: opts[:query] + } + + response = + %Req.Request{ + body: opts[:body], + method: opts[:method] || :get, + url: url + } + |> Req.Request.put_header("x-typesense-api-key", conn.api_key) + |> Req.Request.append_error_steps(retry: &Req.Steps.retry/1) + |> Req.Steps.encode_body() + |> Req.Request.run!() + + case response.status in 200..299 do + true -> + {:ok, Jason.decode!(response.body)} + + false -> + {:error, Jason.decode!(response.body)["message"]} + end + end + @doc """ Req client. @@ -32,6 +89,7 @@ defmodule ExTypesense.HttpClient do } """ @doc since: "0.1.0" + @deprecated "Use request/2 instead" @spec run(request_method(), request_path(), request_body(), map()) :: {:ok, map()} | {:error, map()} def run(request_method, request_path, body \\ nil, query \\ %{}) do @@ -49,7 +107,7 @@ defmodule ExTypesense.HttpClient do method: request_method, url: url } - |> Req.Request.put_header("X-TYPESENSE-API-KEY", api_key()) + |> Req.Request.put_header("x-typesense-api-key", api_key()) |> Req.Request.append_error_steps(retry: &Req.Steps.retry/1) |> Req.Steps.encode_body() |> Req.Request.run!() @@ -63,6 +121,8 @@ defmodule ExTypesense.HttpClient do end end + @doc since: "0.3.0" + @deprecated "Use request/2 instead" @spec httpc_run(URI.__struct__(), atom(), String.t(), list()) :: {:ok, map()} | {:error, map()} def httpc_run(uri, method, payload, content_type \\ ~c"application/json") do uri = %URI{ diff --git a/linux.yml b/linux.yml new file mode 100644 index 0000000..9e6dab9 --- /dev/null +++ b/linux.yml @@ -0,0 +1,10 @@ +services: + typesense: + image: docker.io/typesense/typesense:26.0 + container_name: typesense + restart: on-failure + ports: + - "8108:8108" + volumes: + - ./typesense-data:/data + command: '--data-dir /data --api-key=xyz --enable-cors' diff --git a/mix.exs b/mix.exs index bd853af..d9b3746 100644 --- a/mix.exs +++ b/mix.exs @@ -2,7 +2,7 @@ defmodule ExTypesense.MixProject do use Mix.Project @source_url "https://github.com/jaeyson/ex_typesense" - @version "0.3.5" + @version "0.4.0" def project do [ @@ -36,10 +36,10 @@ defmodule ExTypesense.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ - {:ex_doc, "~> 0.31.2", only: [:dev, :test], runtime: false}, + {:ex_doc, "~> 0.32", only: [:dev, :test], runtime: false}, {:credo, "~> 1.7.0", only: [:dev, :test], runtime: false}, {:req, "~> 0.4.14"}, - {:ecto, "~> 3.11.2"}, + {:ecto, "~> 3.11"}, {:excoveralls, "~> 0.10", only: :test} ] end diff --git a/mix.lock b/mix.lock index 562c317..eedacc8 100644 --- a/mix.lock +++ b/mix.lock @@ -1,34 +1,34 @@ %{ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "castore": {:hex, :castore, "1.0.6", "ffc42f110ebfdafab0ea159cd43d31365fa0af0ce4a02ecebf1707ae619ee727", [:mix], [], "hexpm", "374c6e7ca752296be3d6780a6d5b922854ffcc74123da90f2f328996b962d33a"}, + "castore": {:hex, :castore, "1.0.7", "b651241514e5f6956028147fe6637f7ac13802537e895a724f90bf3e36ddd1dd", [:mix], [], "hexpm", "da7785a4b0d2a021cd1292a60875a784b6caef71e76bf4917bdee1f390455cf5"}, "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, "coverex": {:hex, :coverex, "1.5.0", "a4248302f09562993041f1b056866bfd5688d3a03c429de80c47ea6663989ecc", [:mix], [{:hackney, "~> 1.5", [hex: :hackney, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "21a8f6e734a277b86c01b3cc44b7cc8b77cb1886adbebb708eefc6398567dcac"}, - "credo": {:hex, :credo, "1.7.5", "643213503b1c766ec0496d828c90c424471ea54da77c8a168c725686377b9545", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "f799e9b5cd1891577d8c773d245668aa74a2fcd15eb277f51a0131690ebfb3fd"}, + "credo": {:hex, :credo, "1.7.6", "b8f14011a5443f2839b04def0b252300842ce7388f3af177157c86da18dfbeea", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "146f347fb9f8cbc5f7e39e3f22f70acbef51d441baa6d10169dd604bfbc55296"}, "db_connection": {:hex, :db_connection, "2.5.0", "bb6d4f30d35ded97b29fe80d8bd6f928a1912ca1ff110831edcd238a1973652c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c92d5ba26cd69ead1ff7582dbb860adeedfff39774105a4f1c92cbb654b55aa2"}, "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, "ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"}, "ecto_sql": {:hex, :ecto_sql, "3.10.1", "6ea6b3036a0b0ca94c2a02613fd9f742614b5cfe494c41af2e6571bb034dd94c", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f6a25bdbbd695f12c8171eaff0851fa4c8e72eec1e98c7364402dda9ce11c56b"}, - "ex_doc": {:hex, :ex_doc, "0.31.2", "8b06d0a5ac69e1a54df35519c951f1f44a7b7ca9a5bb7a260cd8a174d6322ece", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "317346c14febaba9ca40fd97b5b5919f7751fb85d399cc8e7e8872049f37e0af"}, + "ex_doc": {:hex, :ex_doc, "0.32.2", "f60bbeb6ccbe75d005763e2a328e6f05e0624232f2393bc693611c2d3ae9fa0e", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "a4480305cdfe7fdfcbb77d1092c76161626d9a7aa4fb698aee745996e34602df"}, "excoveralls": {:hex, :excoveralls, "0.18.1", "a6f547570c6b24ec13f122a5634833a063aec49218f6fff27de9df693a15588c", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d65f79db146bb20399f23046015974de0079668b9abb2f5aac074d078da60b8d"}, "faker": {:hex, :faker, "0.17.0", "671019d0652f63aefd8723b72167ecdb284baf7d47ad3a82a15e9b8a6df5d1fa", [:mix], [], "hexpm", "a7d4ad84a93fd25c5f5303510753789fc2433ff241bf3b4144d3f6f291658a6a"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"}, "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~> 2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"}, - "hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"}, + "hpax": {:hex, :hpax, "0.2.0", "5a58219adcb75977b2edce5eb22051de9362f08236220c9e859a47111c194ff5", [:mix], [], "hexpm", "bea06558cdae85bed075e6c036993d43cd54d447f76d8190a8db0dc5893fa2f1"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, - "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, + "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, - "mint": {:hex, :mint, "1.5.2", "4805e059f96028948870d23d7783613b7e6b0e2fb4e98d720383852a760067fd", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "d77d9e9ce4eb35941907f1d3df38d8f750c357865353e21d335bdcdf6d892a02"}, + "mint": {:hex, :mint, "1.6.0", "88a4f91cd690508a04ff1c3e28952f322528934be541844d54e0ceb765f01d5e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "3c5ae85d90a5aca0a49c0d8b67360bbe407f3b54f1030a111047ff988e8fefaa"}, "nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"}, "nimble_ownership": {:hex, :nimble_ownership, "0.3.1", "99d5244672fafdfac89bfad3d3ab8f0d367603ce1dc4855f86a1c75008bce56f", [:mix], [], "hexpm", "4bf510adedff0449a1d6e200e43e57a814794c8b5b6439071274d248d272a549"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, - "nimble_pool": {:hex, :nimble_pool, "1.0.0", "5eb82705d138f4dd4423f69ceb19ac667b3b492ae570c9f5c900bb3d2f50a847", [:mix], [], "hexpm", "80be3b882d2d351882256087078e1b1952a28bf98d0a287be87e4a24a710b67a"}, + "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"}, "req": {:hex, :req, "0.4.14", "103de133a076a31044e5458e0f850d5681eef23dfabf3ea34af63212e3b902e2", [:mix], [{:aws_signature, "~> 0.3.2", [hex: :aws_signature, repo: "hexpm", optional: true]}, {:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:nimble_ownership, "~> 0.2.0 or ~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "2ddd3d33f9ab714ced8d3c15fd03db40c14dbf129003c4a3eb80fac2cc0b1b08"}, diff --git a/osx.yml b/osx.yml new file mode 100644 index 0000000..a4e7d98 --- /dev/null +++ b/osx.yml @@ -0,0 +1,10 @@ +services: + typesense: + image: docker.io/typesense/typesense:26.0-arm64 + container_name: typesense + restart: on-failure + ports: + - "8108:8108" + volumes: + - ./typesense-data:/data + command: '--data-dir /data --api-key=xyz --enable-cors' diff --git a/test/cluster_test.exs b/test/cluster_test.exs index ce25907..f5e7c54 100644 --- a/test/cluster_test.exs +++ b/test/cluster_test.exs @@ -2,28 +2,25 @@ defmodule ClusterTest do use ExUnit.Case, async: false setup_all do - [{:api_key, "xyz"}, {:host, "localhost"}, {:port, 8108}, {:scheme, "http"}] - |> Enum.each(fn {key, val} -> Application.put_env(:ex_typesense, key, val) end) - - on_exit(fn -> - [:api_key, :host, :port, :scheme] - |> Enum.each(&Application.delete_env(:ex_typesense, &1)) - end) - - %{} + %{ + conn: %ExTypesense.Connection{ + host: "localhost", + api_key: "xyz", + port: 8108, + scheme: "http" + } + } end - test "health" do - assert {:ok, true} === ExTypesense.health() + test "health", context do + assert {:ok, true} = ExTypesense.health(context.conn) end - test "api status" do - api_status = ExTypesense.api_stats() - assert :ok === elem(api_status, 0) + test "api status", context do + assert {:ok, _} = ExTypesense.api_stats(context.conn) end - test "cluster metrics" do - cluster_metrics = ExTypesense.cluster_metrics() - assert :ok === elem(cluster_metrics, 0) + test "cluster metrics", context do + assert {:ok, _} = ExTypesense.cluster_metrics(context.conn) end end diff --git a/test/collection_test.exs b/test/collection_test.exs index 3f9903c..263fe65 100644 --- a/test/collection_test.exs +++ b/test/collection_test.exs @@ -2,8 +2,12 @@ defmodule CollectionTest do use ExUnit.Case, async: false setup_all do - [{:api_key, "xyz"}, {:host, "localhost"}, {:port, 8108}, {:scheme, "http"}] - |> Enum.each(fn {key, val} -> Application.put_env(:ex_typesense, key, val) end) + conn = %ExTypesense.Connection{ + host: "localhost", + api_key: "xyz", + port: 8108, + scheme: "http" + } schema = %{ name: "companies", @@ -15,38 +19,31 @@ defmodule CollectionTest do default_sorting_field: "company_id" } - on_exit(fn -> - ExTypesense.drop_collection(schema.name) - - [:api_key, :host, :port, :scheme] - |> Enum.each(&Application.delete_env(:ex_typesense, &1)) - end) - - %{schema: schema} + %{conn: conn, schema: schema} end - test "success: create and drop collection", %{schema: schema} do - collection = ExTypesense.create_collection(schema) + test "success: create and drop collection", %{conn: conn, schema: schema} do + collection = ExTypesense.create_collection(conn, schema) assert %ExTypesense.Collection{} = collection - ExTypesense.drop_collection(schema.name) - assert {:error, "Not Found"} === ExTypesense.get_collection(schema.name) + ExTypesense.drop_collection(conn, schema.name) + assert {:error, "Not Found"} === ExTypesense.get_collection(conn, schema.name) end - test "error: dropping unknown collection", %{schema: schema} do + test "error: dropping unknown collection", %{conn: conn, schema: schema} do message = ~s(No collection with name `#{schema.name}` found.) - assert {:error, message} === ExTypesense.drop_collection(schema.name) + assert {:error, message} === ExTypesense.drop_collection(conn, schema.name) end test "success: get specific collection" do end - test "error: get unknown collection", %{schema: schema} do - assert {:error, "Not Found"} === ExTypesense.get_collection(schema.name) + test "error: get unknown collection", %{conn: conn, schema: schema} do + assert {:error, "Not Found"} === ExTypesense.get_collection(conn, schema.name) end - test "success: update schema fields", %{schema: schema} do - ExTypesense.create_collection(schema) + test "success: update schema fields", %{conn: conn, schema: schema} do + ExTypesense.create_collection(conn, schema) fields = %{ fields: [ @@ -55,67 +52,67 @@ defmodule CollectionTest do ] } - ExTypesense.update_collection_fields(schema.name, fields) + ExTypesense.update_collection_fields(conn, schema.name, fields) - collection = ExTypesense.get_collection(schema.name) + collection = ExTypesense.get_collection(conn, schema.name) test = Enum.find(collection.fields, fn map -> map["name"] === "test" end) assert test["name"] === "test" - ExTypesense.drop_collection(schema.name) + ExTypesense.drop_collection(conn, schema.name) end # test "success: update collection name" do # end - test "success: count list of collections", %{schema: schema} do - ExTypesense.create_collection(schema) - refute ExTypesense.list_collections() |> Enum.empty?() - ExTypesense.drop_collection(schema.name) + test "success: count list of collections", %{conn: conn, schema: schema} do + ExTypesense.create_collection(conn, schema) + refute ExTypesense.list_collections(conn) |> Enum.empty?() + ExTypesense.drop_collection(conn, schema.name) end - test "success: list aliases", %{schema: schema} do - ExTypesense.upsert_collection_alias(schema.name, schema.name) - count = length(ExTypesense.list_collection_aliases()) + test "success: list aliases", %{conn: conn, schema: schema} do + ExTypesense.upsert_collection_alias(conn, schema.name, schema.name) + count = length(ExTypesense.list_collection_aliases(conn)) assert count === 1 - ExTypesense.delete_collection_alias(schema.name) + ExTypesense.delete_collection_alias(conn, schema.name) end - test "success: create and delete alias", %{schema: schema} do - collection_alias = ExTypesense.upsert_collection_alias(schema.name, schema.name) + test "success: create and delete alias", %{conn: conn, schema: schema} do + collection_alias = ExTypesense.upsert_collection_alias(conn, schema.name, schema.name) assert is_map(collection_alias) === true assert Enum.empty?(collection_alias) === false - ExTypesense.delete_collection_alias(schema.name) - assert {:error, "Not Found"} === ExTypesense.get_collection_alias(schema.name) + ExTypesense.delete_collection_alias(conn, schema.name) + assert {:error, "Not Found"} === ExTypesense.get_collection_alias(conn, schema.name) end - test "success: get collection name by alias", %{schema: schema} do + test "success: get collection name by alias", %{conn: conn, schema: schema} do %{"collection_name" => collection_name, "name" => collection_alias} = - ExTypesense.upsert_collection_alias(schema.name, schema.name) + ExTypesense.upsert_collection_alias(conn, schema.name, schema.name) - assert collection_name === ExTypesense.get_collection_name(collection_alias) + assert collection_name === ExTypesense.get_collection_name(conn, collection_alias) - ExTypesense.delete_collection_alias(schema.name) + ExTypesense.delete_collection_alias(conn, schema.name) end - test "success: get specific alias", %{schema: schema} do - ExTypesense.upsert_collection_alias(schema.name, schema.name) - collection_alias = ExTypesense.get_collection_alias(schema.name) + test "success: get specific alias", %{conn: conn, schema: schema} do + ExTypesense.upsert_collection_alias(conn, schema.name, schema.name) + collection_alias = ExTypesense.get_collection_alias(conn, schema.name) assert is_map(collection_alias) assert collection_alias["name"] === schema.name - ExTypesense.delete_collection_alias(schema.name) + ExTypesense.delete_collection_alias(conn, schema.name) end - test "error: get unknown alias", %{schema: schema} do - assert {:error, "Not Found"} === ExTypesense.get_collection_alias(schema.name) + test "error: get unknown alias", %{conn: conn, schema: schema} do + assert {:error, "Not Found"} === ExTypesense.get_collection_alias(conn, schema.name) end - test "error: delete unknown alias", %{schema: schema} do - assert {:error, "Not Found"} === ExTypesense.delete_collection_alias(schema.name) + test "error: delete unknown alias", %{conn: conn, schema: schema} do + assert {:error, "Not Found"} === ExTypesense.delete_collection_alias(conn, schema.name) end end From d95b986f4e1089ee352ae0227ee47adc1be24422 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Tue, 21 May 2024 14:37:45 +0800 Subject: [PATCH 02/34] add option to add map instead of connection struct --- coveralls.json | 6 ++++++ lib/ex_typesense/connection.ex | 11 ++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 coveralls.json diff --git a/coveralls.json b/coveralls.json new file mode 100644 index 0000000..03632c9 --- /dev/null +++ b/coveralls.json @@ -0,0 +1,6 @@ +{ + "skip_files": [ + "test", + "deps" + ] +} \ No newline at end of file diff --git a/lib/ex_typesense/connection.ex b/lib/ex_typesense/connection.ex index d6c5fc2..c616206 100644 --- a/lib/ex_typesense/connection.ex +++ b/lib/ex_typesense/connection.ex @@ -1,4 +1,9 @@ defmodule ExTypesense.Connection do + @moduledoc since: "0.4.0" + @moduledoc """ + Fetches credentials either from application env or map. + """ + @derive {Inspect, except: [:api_key]} defstruct host: ExTypesense.HttpClient.get_host(), api_key: ExTypesense.HttpClient.api_key(), @@ -7,7 +12,11 @@ defmodule ExTypesense.Connection do @type t() :: %__MODULE__{} - @spec new(connection :: struct()) :: ExTypesense.Connection.t() + @doc """ + Fetches credentials either from application env or map. + """ + @doc since: "0.4.0" + @spec new(connection :: struct() | map()) :: ExTypesense.Connection.t() def new(connection \\ %__MODULE__{}) when is_struct(connection) do %__MODULE__{ host: Map.get(connection, :host), From 09ba0153a4b395c0e87d74a3758b9ce22e6ab637 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Tue, 21 May 2024 15:47:44 +0800 Subject: [PATCH 03/34] add connection_test.exs --- lib/ex_typesense/test_schema/credential.ex | 12 ++++++ test/collection_test.exs | 15 ++++++- test/connection_test.exs | 48 ++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 lib/ex_typesense/test_schema/credential.ex create mode 100644 test/connection_test.exs diff --git a/lib/ex_typesense/test_schema/credential.ex b/lib/ex_typesense/test_schema/credential.ex new file mode 100644 index 0000000..9366a6a --- /dev/null +++ b/lib/ex_typesense/test_schema/credential.ex @@ -0,0 +1,12 @@ +defmodule ExTypesense.TestSchema.Credential do + use Ecto.Schema + + @moduledoc false + + schema "credentials" do + field(:node, :string) + field(:secret_key, :string) + field(:port, :integer) + field(:scheme, :string) + end +end diff --git a/test/collection_test.exs b/test/collection_test.exs index 263fe65..295435a 100644 --- a/test/collection_test.exs +++ b/test/collection_test.exs @@ -35,7 +35,20 @@ defmodule CollectionTest do assert {:error, message} === ExTypesense.drop_collection(conn, schema.name) end - test "success: get specific collection" do + test "success: get specific collection", %{conn: conn} do + schema = %{ + name: "specific_collection", + fields: [ + %{name: "collection_name", type: "string"}, + %{name: "collection_id", type: "int32"} + ], + default_sorting_field: "collection_id" + } + + assert %ExTypesense.Collection{} = ExTypesense.create_collection(conn, schema) + collection = ExTypesense.get_collection(conn, schema.name) + assert collection.name === schema.name + ExTypesense.drop_collection(conn, schema.name) end test "error: get unknown collection", %{conn: conn, schema: schema} do diff --git a/test/connection_test.exs b/test/connection_test.exs new file mode 100644 index 0000000..e5d7056 --- /dev/null +++ b/test/connection_test.exs @@ -0,0 +1,48 @@ +defmodule ConnectionTest do + use ExUnit.Case, async: false + alias ExTypesense.TestSchema.Credential + + setup_all do + %{ + conn: %ExTypesense.Connection{ + host: "localhost", + api_key: "xyz", + port: 8108, + scheme: "http" + } + } + end + + test "Using connection struct", context do + assert {:ok, true} = ExTypesense.health(context.conn) + end + + test "Using map" do + conn = %{ + host: "localhost", + api_key: "xyz", + port: 8108, + scheme: "http" + } + + assert {:ok, true} = ExTypesense.health(conn) + end + + test "Using a struct converted to map and update its keys" do + conn = %Credential{ + node: "localhost", + secret_key: "xyz", + port: 8108, + scheme: "http" + } + + conn = + conn + |> Map.from_struct() + |> Map.drop([:node, :secret_key]) + |> Map.put(:host, conn.node) + |> Map.put(:api_key, conn.secret_key) + + assert {:ok, true} = ExTypesense.health(conn) + end +end From aa469e323a6ddafa5c9d053c70a11fc516ec730e Mon Sep 17 00:00:00 2001 From: jaeyson Date: Tue, 21 May 2024 15:48:43 +0800 Subject: [PATCH 04/34] add coveralls.io test coverage badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 508d1b8..0130ea8 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Hexdocs.pm](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/ex_typesense) [![Hex.pm](https://img.shields.io/hexpm/l/ex_typesense)](LICENSE) [![Typesense badge](https://img.shields.io/badge/Typesense-v26.0-darkblue)](https://typesense.org/docs/26.0/api) +[![Coverage Status](https://coveralls.io/repos/github/jaeyson/ex_typesense/badge.svg?branch=main)](https://coveralls.io/github/jaeyson/ex_typesense?branch=main) Typesense client for Elixir with support for your Ecto schemas. From 93633d1addb2d49cbd5b12b3e01368c9d2181190 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Wed, 22 May 2024 14:20:19 +0800 Subject: [PATCH 05/34] update functions to use default connection --- lib/ex_typesense.ex | 36 ++++-- lib/ex_typesense/collection.ex | 2 +- lib/ex_typesense/document.ex | 201 ++++++++++++++++++++++++-------- lib/ex_typesense/http_client.ex | 11 +- lib/ex_typesense/search.ex | 15 ++- test/document_test.exs | 128 +++++++++++++------- test/search_test.exs | 37 +++--- 7 files changed, 305 insertions(+), 125 deletions(-) diff --git a/lib/ex_typesense.ex b/lib/ex_typesense.ex index 310ffb7..e4bb640 100644 --- a/lib/ex_typesense.ex +++ b/lib/ex_typesense.ex @@ -79,18 +79,38 @@ defmodule ExTypesense do to: ExTypesense.Collection # document-specific tasks - defdelegate get_document(collection_name, document_id), to: ExTypesense.Document - defdelegate create_document(document), to: ExTypesense.Document + defdelegate get_document(conn \\ Connection.new(), collection_name, document_id), + to: ExTypesense.Document + + defdelegate create_document(conn \\ Connection.new(), document), to: ExTypesense.Document + + @deprecated "use delete_document_by_id/3" defdelegate delete_document(document), to: ExTypesense.Document + + @deprecated "use delete_document_by_struct/2" defdelegate delete_document(collection_name, document_id), to: ExTypesense.Document - defdelegate update_document(document), to: ExTypesense.Document - defdelegate upsert_document(document), to: ExTypesense.Document - defdelegate index_multiple_documents(documents), to: ExTypesense.Document - defdelegate update_multiple_documents(documents), to: ExTypesense.Document - defdelegate upsert_multiple_documents(documents), to: ExTypesense.Document + + defdelegate delete_document_by_struct(conn \\ Connection.new(), struct), + to: ExTypesense.Document + + defdelegate delete_document_by_id(conn \\ Connection.new(), collection_name, document_id), + to: ExTypesense.Document + + defdelegate update_document(conn \\ Connection.new(), document), to: ExTypesense.Document + defdelegate upsert_document(conn \\ Connection.new(), document), to: ExTypesense.Document + + defdelegate index_multiple_documents(conn \\ Connection.new(), documents), + to: ExTypesense.Document + + defdelegate update_multiple_documents(conn \\ Connection.new(), documents), + to: ExTypesense.Document + + defdelegate upsert_multiple_documents(conn \\ Connection.new(), documents), + to: ExTypesense.Document # search - defdelegate search(collection_name, params), to: ExTypesense.Search + defdelegate search(conn \\ Connection.new(), module_or_collection_name, params), + to: ExTypesense.Search # geo search diff --git a/lib/ex_typesense/collection.ex b/lib/ex_typesense/collection.ex index d1617aa..afceff8 100644 --- a/lib/ex_typesense/collection.ex +++ b/lib/ex_typesense/collection.ex @@ -6,8 +6,8 @@ defmodule ExTypesense.Collection do In Typesense, a [Collection](https://typesense.org/docs/latest/api/collections.html) is a group of related [Documents](https://typesense.org/docs/latest/api/documents.html) that is roughly equivalent to a table in a relational database. When we create a collection, we give it a name and describe the fields that will be indexed when a document is added to the collection. """ - alias ExTypesense.HttpClient alias ExTypesense.Connection + alias ExTypesense.HttpClient defmodule Field do @moduledoc since: "0.1.0" diff --git a/lib/ex_typesense/document.ex b/lib/ex_typesense/document.ex index 49185c9..6d5ffb4 100644 --- a/lib/ex_typesense/document.ex +++ b/lib/ex_typesense/document.ex @@ -4,6 +4,7 @@ defmodule ExTypesense.Document do Module for CRUD operations for documents. Refer to this [doc guide](https://typesense.org/docs/latest/api/documents.html). """ + alias ExTypesense.Connection alias ExTypesense.HttpClient import Ecto.Query, warn: false @@ -40,20 +41,22 @@ defmodule ExTypesense.Document do } """ @doc since: "0.1.0" - @spec get_document(module() | String.t(), integer()) :: response() - def get_document(module_name, document_id) + @spec get_document(Connection.t(), module() | String.t(), integer()) :: response() + def get_document(conn \\ Connection.new(), module_name, document_id) + + def get_document(conn, module_name, document_id) when is_atom(module_name) and is_integer(document_id) do collection_name = module_name.__schema__(:source) - do_get_document(collection_name, document_id) + do_get_document(conn, collection_name, document_id) end - def get_document(collection_name, document_id) + def get_document(conn, collection_name, document_id) when is_binary(collection_name) and is_integer(document_id) do - do_get_document(collection_name, document_id) + do_get_document(conn, collection_name, document_id) end - @spec do_get_document(String.t() | module(), integer()) :: response() - defp do_get_document(collection_name, document_id) do + @spec do_get_document(Connection.t(), String.t() | module(), integer()) :: response() + defp do_get_document(conn, collection_name, document_id) do path = [ @collections_path, @@ -63,7 +66,7 @@ defmodule ExTypesense.Document do ] |> Path.join() - HttpClient.run(:get, path) + HttpClient.request(conn, %{method: :get, path: path}) end @doc """ @@ -91,16 +94,21 @@ defmodule ExTypesense.Document do {:ok, [%{"success" => true}, %{"success" => true}]} """ @doc since: "0.1.0" - @spec index_multiple_documents(list(struct()) | map()) :: response() - def index_multiple_documents([struct | _] = list_of_structs) + @spec index_multiple_documents(Connection.t(), list(struct()) | map()) :: response() + def index_multiple_documents(conn \\ Connection.new(), list_of_structs) + + def index_multiple_documents(conn, [struct | _] = list_of_structs) when is_struct(struct) do collection_name = struct.__struct__.__schema__(:source) - do_index_multiple_documents(collection_name, "create", list_of_structs) + do_index_multiple_documents(conn, collection_name, "create", list_of_structs) end - def index_multiple_documents(%{collection_name: collection_name, documents: documents} = map) + def index_multiple_documents( + conn, + %{collection_name: collection_name, documents: documents} = map + ) when is_map(map) do - do_index_multiple_documents(collection_name, "create", documents) + do_index_multiple_documents(conn, collection_name, "create", documents) end @doc """ @@ -137,15 +145,20 @@ defmodule ExTypesense.Document do {:ok, [%{"success" => true}, %{"success" => true}]} """ @doc since: "0.3.0" - @spec update_multiple_documents(list(struct()) | map()) :: response() - def update_multiple_documents([struct | _] = list_of_structs) when is_struct(struct) do + @spec update_multiple_documents(Connection.t(), list(struct()) | map()) :: response() + def update_multiple_documents(conn \\ Connection.new(), list_of_structs) + + def update_multiple_documents(conn, [struct | _] = list_of_structs) when is_struct(struct) do collection_name = struct.__struct__.__schema__(:source) - do_index_multiple_documents(collection_name, "update", list_of_structs) + do_index_multiple_documents(conn, collection_name, "update", list_of_structs) end - def update_multiple_documents(%{collection_name: collection_name, documents: documents} = map) + def update_multiple_documents( + conn, + %{collection_name: collection_name, documents: documents} = map + ) when is_map(map) do - do_index_multiple_documents(collection_name, "update", documents) + do_index_multiple_documents(conn, collection_name, "update", documents) end @doc """ @@ -176,22 +189,30 @@ defmodule ExTypesense.Document do {:ok, [%{"success" => true}, %{"success" => true}]} """ @doc since: "0.3.0" - @spec upsert_multiple_documents(map()) :: response() - def upsert_multiple_documents(%{collection_name: collection_name, documents: documents} = map) + @spec upsert_multiple_documents(Connection.t(), map()) :: response() + def upsert_multiple_documents(conn \\ Connection.new(), map) + + def upsert_multiple_documents( + conn, + %{collection_name: collection_name, documents: documents} = map + ) when is_map(map) do - do_index_multiple_documents(collection_name, "upsert", documents) + do_index_multiple_documents(conn, collection_name, "upsert", documents) end - @spec do_index_multiple_documents(String.t(), String.t(), [struct()] | [map()]) :: response() - defp do_index_multiple_documents(collection_name, action, documents) do - payload = - documents - |> Stream.map(&Jason.encode!/1) - |> Enum.join("\n") - - path = Path.join([@collections_path, collection_name, @documents_path, @import_path]) - uri = %URI{path: path, query: "action=#{action}"} - HttpClient.httpc_run(uri, :post, payload, ~c"text/plain") + def upsert_multiple_documents(_, _), + do: {:error, ~s(It should be type of map, ":documents" key should contain list of maps)} + + @spec do_index_multiple_documents(Connection.t(), String.t(), String.t(), [struct()] | [map()]) :: + response() + defp do_index_multiple_documents(conn, collection_name, action, documents) do + HttpClient.request(conn, %{ + method: :post, + path: Path.join([@collections_path, collection_name, @documents_path, @import_path]), + query: %{action: action}, + body: Enum.map_join(documents, "\n", &Jason.encode!/1), + content_type: "text/plain" + }) end @doc """ @@ -228,18 +249,20 @@ defmodule ExTypesense.Document do } """ @doc since: "0.3.0" - @spec create_document(struct() | map() | [struct()] | [map()]) :: response() - def create_document(struct) when is_struct(struct) do + @spec create_document(Connection.t(), struct() | map() | [struct()] | [map()]) :: response() + def create_document(conn \\ Connection.new(), struct) + + def create_document(conn, struct) when is_struct(struct) do collection_name = struct.__struct__.__schema__(:source) path = Path.join([@collections_path, collection_name, @documents_path]) - payload = Map.put(struct, :id, to_string(struct.id)) |> Jason.encode!() - do_index_document(path, :post, "create", payload) + payload = Jason.encode!(struct) + do_index_document(conn, path, :post, "create", payload) end - def create_document(document) when is_map(document) do + def create_document(conn, document) when is_map(document) do collection_name = Map.get(document, :collection_name) path = Path.join([@collections_path, collection_name, @documents_path]) - do_index_document(path, :post, "create", Jason.encode!(document)) + do_index_document(conn, path, :post, "create", Jason.encode!(document)) end @doc """ @@ -281,21 +304,23 @@ defmodule ExTypesense.Document do } """ @doc since: "0.3.0" - @spec update_document(struct() | map()) :: response() - def update_document(struct) when is_struct(struct) do + @spec update_document(Connection.t(), struct() | map()) :: response() + def update_document(conn \\ Connection.new(), struct) + + def update_document(conn, struct) when is_struct(struct) do collection_name = struct.__struct__.__schema__(:source) path = Path.join([@collections_path, collection_name, @documents_path, Jason.encode!(struct.id)]) - do_index_document(path, :patch, "update", Jason.encode!(struct)) + do_index_document(conn, path, :patch, "update", Jason.encode!(struct)) end - def update_document(document) when is_map(document) do + def update_document(conn, document) when is_map(document) do id = String.to_integer(document.id) collection_name = Map.get(document, :collection_name) path = Path.join([@collections_path, collection_name, @documents_path, Jason.encode!(id)]) - do_index_document(path, :patch, "update", Jason.encode!(document)) + do_index_document(conn, path, :patch, "update", Jason.encode!(document)) end @doc """ @@ -305,16 +330,18 @@ defmodule ExTypesense.Document do """ @doc since: "0.3.0" - @spec upsert_document(map() | struct()) :: response() - def upsert_document(struct) when is_struct(struct) do + @spec upsert_document(Connection.t(), map() | struct()) :: response() + def upsert_document(conn \\ Connection.new(), struct) + + def upsert_document(conn, struct) when is_struct(struct) do id = to_string(struct.id) collection_name = struct.__struct__.__schema__(:source) document = Map.put(struct, :id, id) |> Jason.encode!() path = Path.join([@collections_path, collection_name, @documents_path]) - do_index_document(path, :post, "upsert", document) + do_index_document(conn, path, :post, "upsert", document) end - def upsert_document(document) when is_map(document) do + def upsert_document(conn, document) when is_map(document) do collection_name = Map.get(document, :collection_name) id = document.id @@ -326,18 +353,88 @@ defmodule ExTypesense.Document do end path = Path.join([@collections_path, collection_name, @documents_path]) - do_index_document(path, :post, "upsert", document) + do_index_document(conn, path, :post, "upsert", document) end - @spec do_index_document(String.t(), atom(), String.t(), String.t()) :: response() - defp do_index_document(path, method, action, document) do - uri = %URI{path: path, query: "action=#{action}"} - HttpClient.httpc_run(uri, method, document) + @spec do_index_document(Connection.t(), String.t(), atom(), String.t(), String.t()) :: + response() + defp do_index_document(conn, path, method, action, document) do + opts = %{ + method: method, + path: path, + query: %{action: action}, + body: document + } + + HttpClient.request(conn, opts) end @doc """ Deletes a document by struct. """ + @doc since: "0.4.0" + @spec delete_document_by_struct(Connection.t(), struct()) :: response() + def delete_document_by_struct(conn \\ Connection.new(), struct) when is_struct(struct) do + document_id = struct.id + collection_name = struct.__struct__.__schema__(:source) + do_delete_document(conn, collection_name, document_id) + end + + @doc """ + Deletes a document by collection name and document id. + + ## Examples + iex> schema = %{ + ...> name: "posts", + ...> fields: [ + ...> %{name: "title", type: "string"} + ...> ], + ...> } + ...> ExTypesense.create_collection(schema) + iex> post = + ...> %{ + ...> id: "12", + ...> collection_name: "posts", + ...> post_id: 22, + ...> title: "the quick brown fox" + ...> } + iex> ExTypesense.create_document(post) + iex> ExTypesense.delete_document("posts", 12) + {:ok, + %{ + "id" => "12", + "post_id" => 22, + "title" => "the quick brown fox", + "collection_name" => "posts" + } + } + """ + @doc since: "0.4.0" + @spec delete_document_by_id(Connection.t(), String.t(), integer()) :: response() + def delete_document_by_id(conn \\ Connection.new(), collection_name, document_id) + when is_binary(collection_name) and is_integer(document_id) do + do_delete_document(conn, collection_name, document_id) + end + + @doc since: "0.4.0" + @spec do_delete_document(Connection.t(), String.t(), integer()) :: response() + defp do_delete_document(conn, collection_name, document_id) do + path = + Path.join([ + @collections_path, + collection_name, + @documents_path, + Jason.encode!(document_id) + ]) + + HttpClient.request(conn, %{method: :delete, path: path}) + end + + @doc """ + Deletes a document by struct. + """ + @doc since: "0.3.0" + @deprecated "use delete_document_by_struct/2" @spec delete_document(struct()) :: response() def delete_document(struct) when is_struct(struct) do document_id = struct.id @@ -375,12 +472,14 @@ defmodule ExTypesense.Document do } """ @doc since: "0.3.0" + @deprecated "use delete_document_by_id/3" @spec delete_document(String.t(), integer()) :: response() def delete_document(collection_name, document_id) when is_binary(collection_name) and is_integer(document_id) do do_delete_document(collection_name, document_id) end + @deprecated "use do_delete_document/3" defp do_delete_document(collection_name, document_id) do path = Path.join([ diff --git a/lib/ex_typesense/http_client.ex b/lib/ex_typesense/http_client.ex index 9c3ae9e..2ad58ea 100644 --- a/lib/ex_typesense/http_client.ex +++ b/lib/ex_typesense/http_client.ex @@ -48,7 +48,7 @@ defmodule ExTypesense.HttpClient do host: conn.host, port: conn.port, path: opts[:path], - query: opts[:query] + query: URI.encode_query(opts[:query] || %{}) } response = @@ -58,13 +58,18 @@ defmodule ExTypesense.HttpClient do url: url } |> Req.Request.put_header("x-typesense-api-key", conn.api_key) + |> Req.Request.put_header("content-type", opts[:content_type] || "application/json") |> Req.Request.append_error_steps(retry: &Req.Steps.retry/1) - |> Req.Steps.encode_body() |> Req.Request.run!() case response.status in 200..299 do true -> - {:ok, Jason.decode!(response.body)} + body = + if opts[:content_type] === "text/plain", + do: String.split(response.body, "\n", trim: true) |> Enum.map(&Jason.decode!/1), + else: Jason.decode!(response.body) + + {:ok, body} false -> {:error, Jason.decode!(response.body)["message"]} diff --git a/lib/ex_typesense/search.ex b/lib/ex_typesense/search.ex index 9973c7a..1ae7412 100644 --- a/lib/ex_typesense/search.ex +++ b/lib/ex_typesense/search.ex @@ -4,6 +4,7 @@ defmodule ExTypesense.Search do Module for searching documents. """ + alias ExTypesense.Connection alias ExTypesense.HttpClient alias ExTypesense.ResultParser import Ecto.Query, warn: false @@ -39,8 +40,10 @@ defmodule ExTypesense.Search do """ @doc since: "0.1.0" - @spec search(module() | String.t(), map()) :: response() - def search(module_name, params) when is_atom(module_name) and is_map(params) do + @spec search(Connection.t(), module() | String.t(), map()) :: response() + def search(conn \\ Connection.new(), module_or_collection_name, params) + + def search(conn, module_name, params) when is_atom(module_name) and is_map(params) do collection_name = module_name.__schema__(:source) path = @@ -51,12 +54,13 @@ defmodule ExTypesense.Search do @search_path ]) - {:ok, result} = HttpClient.run(:get, path, nil, params) + {:ok, result} = HttpClient.request(conn, %{method: :get, path: path, query: params}) + # {:ok, result} = HttpClient.run(:get, path, nil, params) ResultParser.hits_to_query(result["hits"], module_name) end - def search(collection_name, params) when is_binary(collection_name) and is_map(params) do + def search(conn, collection_name, params) when is_binary(collection_name) and is_map(params) do path = Path.join([ @collections_path, @@ -65,6 +69,7 @@ defmodule ExTypesense.Search do @search_path ]) - HttpClient.run(:get, path, nil, params) + HttpClient.request(conn, %{method: :get, path: path, query: params}) + # HttpClient.run(:get, path, nil, params) end end diff --git a/test/document_test.exs b/test/document_test.exs index 2bf4cce..d6d98ef 100644 --- a/test/document_test.exs +++ b/test/document_test.exs @@ -1,10 +1,20 @@ defmodule DocumentTest do use ExUnit.Case, async: false + alias ExTypesense.TestSchema.Person + setup_all do + # this is for deprecated function to set the creds [{:api_key, "xyz"}, {:host, "localhost"}, {:port, 8108}, {:scheme, "http"}] |> Enum.each(fn {key, val} -> Application.put_env(:ex_typesense, key, val) end) + conn = %ExTypesense.Connection{ + host: "localhost", + api_key: "xyz", + port: 8108, + scheme: "http" + } + schema = %{ name: "companies", fields: [ @@ -38,41 +48,49 @@ defmodule DocumentTest do ] } - ExTypesense.create_collection(schema) + ExTypesense.create_collection(conn, schema) + ExTypesense.create_collection(conn, Person) on_exit(fn -> - ExTypesense.drop_collection(schema.name) + ExTypesense.drop_collection(conn, schema.name) + ExTypesense.drop_collection(conn, Person) # from doctest - ExTypesense.drop_collection("posts") + ExTypesense.drop_collection(conn, "posts") + # this is for deprecated function to set the creds [:api_key, :host, :port, :scheme] |> Enum.each(&Application.delete_env(:ex_typesense, &1)) end) - %{schema: schema, document: document, multiple_documents: multiple_documents} + %{conn: conn, schema: schema, document: document, multiple_documents: multiple_documents} end - test "error: get unknown document", %{schema: schema} do + test "error: get unknown document", %{conn: conn, schema: schema} do unknown_id = 999 message = ~s(Could not find a document with id: #{unknown_id}) - assert {:error, message} === ExTypesense.get_document(schema.name, unknown_id) + assert {:error, message} === ExTypesense.get_document(conn, schema.name, unknown_id) end - test "success: index a document using a map then fetch if indexed", %{document: document} do - {:ok, %{"id" => id, "company_name" => company_name}} = ExTypesense.create_document(document) + test "success: index a document using a map then fetch if indexed", %{ + conn: conn, + document: document + } do + {:ok, %{"id" => id, "company_name" => company_name}} = + ExTypesense.create_document(conn, document) + id = String.to_integer(id) - {:ok, result} = ExTypesense.get_document("companies", id) + {:ok, result} = ExTypesense.get_document(conn, "companies", id) assert result["company_name"] === company_name end - test "success: adding unknown field", %{document: document} do + test "success: adding unknown field", %{conn: conn, document: document} do document = Map.put(document, :unknown_field, "unknown_value") - {:ok, collection} = ExTypesense.create_document(document) + {:ok, collection} = ExTypesense.create_document(conn, document) assert Map.has_key?(collection, "unknown_field") end - test "success: upsert to update a document", %{document: document} do - {:ok, %{"id" => id}} = ExTypesense.create_document(document) + test "success: upsert to update a document", %{conn: conn, document: document} do + {:ok, %{"id" => id}} = ExTypesense.create_document(conn, document) id = String.to_integer(id) company_name = "Stark Industries" @@ -81,25 +99,25 @@ defmodule DocumentTest do |> Map.put(:company_name, company_name) |> Map.put(:id, id) - {:ok, result} = ExTypesense.upsert_document(updated_document) + {:ok, result} = ExTypesense.upsert_document(conn, updated_document) assert company_name === result["company_name"] end - test "success: create document using upsert_document/2", %{document: document} do + test "success: create document using upsert_document/2", %{conn: conn, document: document} do document = Map.put(document, :id, "9999") - assert {:ok, %{"id" => "9999"}} = ExTypesense.upsert_document(document) + assert {:ok, %{"id" => "9999"}} = ExTypesense.upsert_document(conn, document) end - test "error: creates a document with a specific id twice", %{document: document} do + test "error: creates a document with a specific id twice", %{conn: conn, document: document} do document = Map.put(document, :id, "99") - assert {:ok, %{"id" => id}} = ExTypesense.create_document(document) - assert {:ok, message} = ExTypesense.create_document(document) - assert message === %{"message" => "A document with id #{id} already exists."} + assert {:ok, %{"id" => id}} = ExTypesense.create_document(conn, document) + assert {:error, message} = ExTypesense.create_document(conn, document) + assert message === "A document with id #{id} already exists." end - test "success: update a document", %{document: document} do - {:ok, %{"id" => id}} = ExTypesense.create_document(document) + test "success: update a document", %{conn: conn, document: document} do + {:ok, %{"id" => id}} = ExTypesense.create_document(conn, document) company_name = "Stark Industries" updated_document = @@ -107,25 +125,46 @@ defmodule DocumentTest do |> Map.put(:company_name, company_name) |> Map.put(:id, id) - {:ok, result} = ExTypesense.update_document(updated_document) + {:ok, result} = ExTypesense.update_document(conn, updated_document) assert company_name === result["company_name"] end - test "success: deletes a document using map", %{document: document} do - {:ok, %{"id" => id}} = ExTypesense.create_document(document) + test "success: (deprecate) deletes a document using map", %{conn: conn, document: document} do + {:ok, %{"id" => id}} = ExTypesense.create_document(conn, document) + + assert {:ok, _} = ExTypesense.delete_document(document.collection_name, String.to_integer(id)) + end + + test "success: deletes a document by struct", %{conn: conn} do + person = %Person{id: 0, name: "Tar Zan", person_id: 0, country: "Brazil"} - ExTypesense.delete_document(document.collection_name, String.to_integer(id)) + assert {:ok, %{"country" => "Brazil", "id" => "0", "name" => "Tar Zan", "person_id" => 0}} = + ExTypesense.create_document(conn, person) + + assert {:ok, _} = ExTypesense.delete_document_by_struct(conn, person) + end + + test "success: deletes a document by id", %{conn: conn, document: document} do + {:ok, %{"id" => id}} = ExTypesense.create_document(conn, document) + + assert {:ok, _} = + ExTypesense.delete_document_by_id( + conn, + document.collection_name, + String.to_integer(id) + ) end - test "success: index multiple documents", %{multiple_documents: multiple_documents} do - multiple_documents - |> ExTypesense.index_multiple_documents() + test "success: index multiple documents", %{conn: conn, multiple_documents: multiple_documents} do + conn + |> ExTypesense.index_multiple_documents(multiple_documents) |> Kernel.===({:ok, [%{"success" => true}, %{"success" => true}]}) |> assert() end test "success: update multiple documents", %{ + conn: conn, multiple_documents: multiple_documents, schema: schema } do @@ -134,8 +173,8 @@ defmodule DocumentTest do first_update = "first_update" second_update = "second_update" - {:ok, %{"id" => first_id}} = ExTypesense.create_document(first) - {:ok, %{"id" => second_id}} = ExTypesense.create_document(second) + {:ok, %{"id" => first_id}} = ExTypesense.create_document(conn, first) + {:ok, %{"id" => second_id}} = ExTypesense.create_document(conn, second) update_1 = first @@ -147,23 +186,34 @@ defmodule DocumentTest do |> Map.put(:id, second_id) |> Map.put(:company_name, second_update) - multiple_documents - |> Map.put(:documents, [update_1, update_2]) - |> ExTypesense.update_multiple_documents() + multiple_documents = Map.put(multiple_documents, :documents, [update_1, update_2]) + + conn + |> ExTypesense.update_multiple_documents(multiple_documents) |> Kernel.===({:ok, [%{"success" => true}, %{"success" => true}]}) |> assert() - {:ok, first} = ExTypesense.get_document(schema.name, String.to_integer(first_id)) - {:ok, second} = ExTypesense.get_document(schema.name, String.to_integer(second_id)) + {:ok, first} = ExTypesense.get_document(conn, schema.name, String.to_integer(first_id)) + {:ok, second} = ExTypesense.get_document(conn, schema.name, String.to_integer(second_id)) assert first["company_name"] === first_update assert second["company_name"] === second_update end - test "success: upsert multiple documents", %{multiple_documents: multiple_documents} do - multiple_documents - |> ExTypesense.upsert_multiple_documents() + test "success: upsert multiple documents", %{conn: conn, multiple_documents: multiple_documents} do + conn + |> ExTypesense.upsert_multiple_documents(multiple_documents) |> Kernel.===({:ok, [%{"success" => true}, %{"success" => true}]}) |> assert() end + + test "error: upsert multiple documents with struct type" do + persons = [ + %Person{id: 1, name: "John Smith"}, + %Person{id: 2, name: "Jane Smith"} + ] + + assert {:error, ~s(It should be type of map, ":documents" key should contain list of maps)} === + ExTypesense.upsert_multiple_documents(persons) + end end diff --git a/test/search_test.exs b/test/search_test.exs index 3e32309..26ea741 100644 --- a/test/search_test.exs +++ b/test/search_test.exs @@ -4,8 +4,12 @@ defmodule SearchTest do alias ExTypesense.TestSchema.Person setup_all do - [{:api_key, "xyz"}, {:host, "localhost"}, {:port, 8108}, {:scheme, "http"}] - |> Enum.each(fn {key, val} -> Application.put_env(:ex_typesense, key, val) end) + conn = %ExTypesense.Connection{ + host: "localhost", + api_key: "xyz", + port: 8108, + scheme: "http" + } schema = %{ name: "companies", @@ -31,48 +35,45 @@ defmodule SearchTest do person_id: 1002 } - ExTypesense.create_collection(schema) - ExTypesense.create_collection(Person) + ExTypesense.create_collection(conn, schema) + ExTypesense.create_collection(conn, Person) - {:ok, _} = ExTypesense.create_document(document) - {:ok, _} = ExTypesense.create_document(person) + {:ok, _} = ExTypesense.create_document(conn, document) + {:ok, _} = ExTypesense.create_document(conn, person) on_exit(fn -> - ExTypesense.drop_collection(schema.name) - ExTypesense.drop_collection(Person) - - [:api_key, :host, :port, :scheme] - |> Enum.each(&Application.delete_env(:ex_typesense, &1)) + ExTypesense.drop_collection(conn, schema.name) + ExTypesense.drop_collection(conn, Person) end) - %{schema: schema, document: document, person: person} + %{conn: conn, schema: schema, document: document, person: person} end - test "success: search with result", %{schema: schema} do + test "success: search with result", %{conn: conn, schema: schema} do params = %{ q: "test", query_by: "company_name" } - assert {:ok, _} = ExTypesense.search(schema.name, params) + assert {:ok, _} = ExTypesense.search(conn, schema.name, params) end - test "success: search with Ecto", %{person: person} do + test "success: search with Ecto", %{conn: conn, person: person} do params = %{ q: "UK", query_by: "country" } assert %Ecto.Query{} = Person |> where([p], p.id in ^[person.person_id]) - assert %Ecto.Query{} = ExTypesense.search(Person, params) + assert %Ecto.Query{} = ExTypesense.search(conn, Person, params) end - test "success: empty result", %{schema: schema} do + test "success: empty result", %{conn: conn, schema: schema} do params = %{ q: "unknown", query_by: "company_name" } - assert {:ok, _} = ExTypesense.search(schema.name, params) + assert {:ok, _} = ExTypesense.search(conn, schema.name, params) end end From 8c0d69113ec311388eafa859a2dcece8bae07d65 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Wed, 22 May 2024 14:30:32 +0800 Subject: [PATCH 06/34] update docs --- lib/ex_typesense/http_client.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ex_typesense/http_client.ex b/lib/ex_typesense/http_client.ex index 2ad58ea..0422c4c 100644 --- a/lib/ex_typesense/http_client.ex +++ b/lib/ex_typesense/http_client.ex @@ -39,7 +39,7 @@ defmodule ExTypesense.HttpClient do }] } """ - @doc since: "0.1.0" + @doc since: "0.4.0" @spec request(Connection.t(), map()) :: nil def request(conn, opts \\ %{}) do url = From 2f252cb85f0821065c970e445f4cfc3218cb641d Mon Sep 17 00:00:00 2001 From: jaeyson Date: Wed, 22 May 2024 14:32:57 +0800 Subject: [PATCH 07/34] update ci.yml --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b096a5a..e45b3e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,6 @@ jobs: mix local.rebar --if-missing mix local.hex --if-missing mix deps.get - mix format --check-formatted - name: Compile project run: mix compile @@ -64,6 +63,9 @@ jobs: - name: run static analysis run: mix credo --all --strict + - name: format files + run: mix format --check-formatted + - name: Run tests run: mix test From df216fd4f89a666b5f3cba8905b7fe39cdfffd89 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Wed, 22 May 2024 14:37:19 +0800 Subject: [PATCH 08/34] update ci.yml --- .github/workflows/ci.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e45b3e7..b64c2e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,14 +57,23 @@ jobs: mix local.hex --if-missing mix deps.get + - name: Find unused dependencies + run: mix deps.unlock --check-unused + + - name: Check retired dependencies + run: mix hex.audit + + - name: Security audit of dependencies + run: mix deps.audit + - name: Compile project - run: mix compile + run: mix compile --all-warnings - name: run static analysis run: mix credo --all --strict - name: format files - run: mix format --check-formatted + run: mix format --dry-run --check-formatted - name: Run tests run: mix test From 17aa1c5d3c39aeca1d3e21641835c89046e6dc6b Mon Sep 17 00:00:00 2001 From: jaeyson Date: Wed, 22 May 2024 14:40:18 +0800 Subject: [PATCH 09/34] update mix.lock --- mix.lock | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/mix.lock b/mix.lock index eedacc8..9cda843 100644 --- a/mix.lock +++ b/mix.lock @@ -1,38 +1,25 @@ %{ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "castore": {:hex, :castore, "1.0.7", "b651241514e5f6956028147fe6637f7ac13802537e895a724f90bf3e36ddd1dd", [:mix], [], "hexpm", "da7785a4b0d2a021cd1292a60875a784b6caef71e76bf4917bdee1f390455cf5"}, - "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, - "coverex": {:hex, :coverex, "1.5.0", "a4248302f09562993041f1b056866bfd5688d3a03c429de80c47ea6663989ecc", [:mix], [{:hackney, "~> 1.5", [hex: :hackney, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "21a8f6e734a277b86c01b3cc44b7cc8b77cb1886adbebb708eefc6398567dcac"}, "credo": {:hex, :credo, "1.7.6", "b8f14011a5443f2839b04def0b252300842ce7388f3af177157c86da18dfbeea", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "146f347fb9f8cbc5f7e39e3f22f70acbef51d441baa6d10169dd604bfbc55296"}, - "db_connection": {:hex, :db_connection, "2.5.0", "bb6d4f30d35ded97b29fe80d8bd6f928a1912ca1ff110831edcd238a1973652c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c92d5ba26cd69ead1ff7582dbb860adeedfff39774105a4f1c92cbb654b55aa2"}, "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, "ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"}, - "ecto_sql": {:hex, :ecto_sql, "3.10.1", "6ea6b3036a0b0ca94c2a02613fd9f742614b5cfe494c41af2e6571bb034dd94c", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f6a25bdbbd695f12c8171eaff0851fa4c8e72eec1e98c7364402dda9ce11c56b"}, - "ex_doc": {:hex, :ex_doc, "0.32.2", "f60bbeb6ccbe75d005763e2a328e6f05e0624232f2393bc693611c2d3ae9fa0e", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "a4480305cdfe7fdfcbb77d1092c76161626d9a7aa4fb698aee745996e34602df"}, + "ex_doc": {:hex, :ex_doc, "0.33.0", "690562b153153c7e4d455dc21dab86e445f66ceba718defe64b0ef6f0bd83ba0", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "3f69adc28274cb51be37d09b03e4565232862a4b10288a3894587b0131412124"}, "excoveralls": {:hex, :excoveralls, "0.18.1", "a6f547570c6b24ec13f122a5634833a063aec49218f6fff27de9df693a15588c", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d65f79db146bb20399f23046015974de0079668b9abb2f5aac074d078da60b8d"}, - "faker": {:hex, :faker, "0.17.0", "671019d0652f63aefd8723b72167ecdb284baf7d47ad3a82a15e9b8a6df5d1fa", [:mix], [], "hexpm", "a7d4ad84a93fd25c5f5303510753789fc2433ff241bf3b4144d3f6f291658a6a"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"}, - "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~> 2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"}, "hpax": {:hex, :hpax, "0.2.0", "5a58219adcb75977b2edce5eb22051de9362f08236220c9e859a47111c194ff5", [:mix], [], "hexpm", "bea06558cdae85bed075e6c036993d43cd54d447f76d8190a8db0dc5893fa2f1"}, - "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, - "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "mint": {:hex, :mint, "1.6.0", "88a4f91cd690508a04ff1c3e28952f322528934be541844d54e0ceb765f01d5e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "3c5ae85d90a5aca0a49c0d8b67360bbe407f3b54f1030a111047ff988e8fefaa"}, "nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"}, "nimble_ownership": {:hex, :nimble_ownership, "0.3.1", "99d5244672fafdfac89bfad3d3ab8f0d367603ce1dc4855f86a1c75008bce56f", [:mix], [], "hexpm", "4bf510adedff0449a1d6e200e43e57a814794c8b5b6439071274d248d272a549"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, - "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, - "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"}, "req": {:hex, :req, "0.4.14", "103de133a076a31044e5458e0f850d5681eef23dfabf3ea34af63212e3b902e2", [:mix], [{:aws_signature, "~> 0.3.2", [hex: :aws_signature, repo: "hexpm", optional: true]}, {:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:nimble_ownership, "~> 0.2.0 or ~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "2ddd3d33f9ab714ced8d3c15fd03db40c14dbf129003c4a3eb80fac2cc0b1b08"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, } From 39d74e7db0556aed2a1fd27485d17f26a175d778 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Wed, 22 May 2024 14:43:06 +0800 Subject: [PATCH 10/34] update mix.exs --- mix.exs | 3 ++- mix.lock | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index d9b3746..3d8bd59 100644 --- a/mix.exs +++ b/mix.exs @@ -40,7 +40,8 @@ defmodule ExTypesense.MixProject do {:credo, "~> 1.7.0", only: [:dev, :test], runtime: false}, {:req, "~> 0.4.14"}, {:ecto, "~> 3.11"}, - {:excoveralls, "~> 0.10", only: :test} + {:excoveralls, "~> 0.10", only: :test}, + {:mix_audit, "~> 2.1", only: [:dev, :test], runtime: false} ] end diff --git a/mix.lock b/mix.lock index 9cda843..f6831f6 100644 --- a/mix.lock +++ b/mix.lock @@ -16,10 +16,13 @@ "makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"}, "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, "mint": {:hex, :mint, "1.6.0", "88a4f91cd690508a04ff1c3e28952f322528934be541844d54e0ceb765f01d5e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "3c5ae85d90a5aca0a49c0d8b67360bbe407f3b54f1030a111047ff988e8fefaa"}, + "mix_audit": {:hex, :mix_audit, "2.1.3", "c70983d5cab5dca923f9a6efe559abfb4ec3f8e87762f02bab00fa4106d17eda", [:make, :mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.9", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "8c3987100b23099aea2f2df0af4d296701efd031affb08d0746b2be9e35988ec"}, "nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"}, "nimble_ownership": {:hex, :nimble_ownership, "0.3.1", "99d5244672fafdfac89bfad3d3ab8f0d367603ce1dc4855f86a1c75008bce56f", [:mix], [], "hexpm", "4bf510adedff0449a1d6e200e43e57a814794c8b5b6439071274d248d272a549"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "req": {:hex, :req, "0.4.14", "103de133a076a31044e5458e0f850d5681eef23dfabf3ea34af63212e3b902e2", [:mix], [{:aws_signature, "~> 0.3.2", [hex: :aws_signature, repo: "hexpm", optional: true]}, {:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:nimble_ownership, "~> 0.2.0 or ~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "2ddd3d33f9ab714ced8d3c15fd03db40c14dbf129003c4a3eb80fac2cc0b1b08"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, + "yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"}, + "yaml_elixir": {:hex, :yaml_elixir, "2.9.0", "9a256da867b37b8d2c1ffd5d9de373a4fda77a32a45b452f1708508ba7bbcb53", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "0cb0e7d4c56f5e99a6253ed1a670ed0e39c13fc45a6da054033928607ac08dfc"}, } From 043d2b6a931bf059d1e7ff2f34b4ad55690ba26c Mon Sep 17 00:00:00 2001 From: jaeyson Date: Wed, 22 May 2024 14:57:21 +0800 Subject: [PATCH 11/34] update module docs in HttpClient.ex --- .github/workflows/ci.yml | 2 +- lib/ex_typesense/http_client.ex | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b64c2e3..778fb9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,7 +73,7 @@ jobs: run: mix credo --all --strict - name: format files - run: mix format --dry-run --check-formatted + run: mix format - name: Run tests run: mix test diff --git a/lib/ex_typesense/http_client.ex b/lib/ex_typesense/http_client.ex index 0422c4c..f0721c1 100644 --- a/lib/ex_typesense/http_client.ex +++ b/lib/ex_typesense/http_client.ex @@ -19,6 +19,14 @@ defmodule ExTypesense.HttpClient do @doc """ Command for making http requests. + ## Options + + - `:body` Payload for passing as request body (defaults to `nil`). + - `:path` Request path. + - `:method` Request method (e.g. `:get`, `:post`, `:put`, `:patch`, `:delete`). Defaults to `:get`. + - `:query` Request query params (defaults to `%{}`). + - `:content_type` `"Content-Type"` request header. Defaults to `"application/json"`. + ## Examples iex> connection = %ExTypesense.Connection{ ...> host: "localhost", From 099c5364c3714aa6ccc9d08192109fa8e36696df Mon Sep 17 00:00:00 2001 From: jaeyson Date: Thu, 23 May 2024 21:15:18 +0800 Subject: [PATCH 12/34] check formatted --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 778fb9d..8b51a54 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,8 +72,8 @@ jobs: - name: run static analysis run: mix credo --all --strict - - name: format files - run: mix format + - name: check format files + run: mix format --check-formatted - name: Run tests run: mix test From 476f3fba66b734126d17d0e69bf39f3a2cc0cbab Mon Sep 17 00:00:00 2001 From: jaeyson Date: Thu, 23 May 2024 21:18:14 +0800 Subject: [PATCH 13/34] update ci.yml --- .github/workflows/ci.yml | 2 +- lib/ex_typesense/http_client.ex | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b51a54..502b2fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: - typesense-version: [0.25.2 26.0] + typesense-version: [0.25.2, 26.0] env: MIX_ENV: test diff --git a/lib/ex_typesense/http_client.ex b/lib/ex_typesense/http_client.ex index f0721c1..fbf4f24 100644 --- a/lib/ex_typesense/http_client.ex +++ b/lib/ex_typesense/http_client.ex @@ -9,6 +9,7 @@ defmodule ExTypesense.HttpClient do @type request_body() :: iodata() | nil @type request_method() :: :get | :post | :delete | :patch | :put @type request_path() :: String.t() + @api_header_name ~c"X-TYPESENSE-API-KEY" def get_host, do: Application.get_env(:ex_typesense, :host) From 8fb2767d89993ff14081938ce688af83f0b8b633 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Thu, 23 May 2024 23:28:02 +0800 Subject: [PATCH 14/34] use container workflow instead of services --- .github/workflows/ci.yml | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 502b2fc..ee2f64f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,16 +20,30 @@ jobs: test: runs-on: ubuntu-latest - - strategy: - matrix: - typesense-version: [0.25.2, 26.0] + container: typesense/typesense:26.0 env: MIX_ENV: test GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # services: + # typesense: + # image: typesense/typesense:${{ matrix.typesense-version }} + # ports: + # - "8108:8108" + # command: '--data-dir /data --api-key=xyz --enable-cors' + steps: + - name: Start Typesense + run: | + docker run -d \ + -p 8108:8108 \ + -v/tmp/typesense:/data \ + --name typesense \ + typesense/typesense:26.0 \ + --api-key=xyz \ + --data-dir /data \ + --enable-cors - uses: actions/checkout@v4 - uses: erlef/setup-beam@v1 with: @@ -45,11 +59,11 @@ jobs: restore-keys: | ${{ runner.os }}-mixtest- - - name: Start Typesense - uses: jirevwe/typesense-github-action@v1.0.1 - with: - typesense-version: ${{ matrix.typesense-version }} - typesense-api-key: xyz + # - name: Start Typesense + # uses: jirevwe/typesense-github-action@v1.0.1 + # with: + # typesense-version: ${{ matrix.typesense-version }} + # typesense-api-key: xyz - name: Install Dependencies run: | From 299130130ce912b063bce3c93e071294f37694f7 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Fri, 24 May 2024 00:01:33 +0800 Subject: [PATCH 15/34] update ci.yml --- .github/workflows/ci.yml | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee2f64f..030a932 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,37 +20,30 @@ jobs: test: runs-on: ubuntu-latest - container: typesense/typesense:26.0 + container: + image: typesense/typesense:26.0 + ports: ['8108:8108'] env: MIX_ENV: test GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # services: - # typesense: - # image: typesense/typesense:${{ matrix.typesense-version }} - # ports: - # - "8108:8108" - # command: '--data-dir /data --api-key=xyz --enable-cors' - steps: - - name: Start Typesense + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup Typesense run: | - docker run -d \ - -p 8108:8108 \ - -v/tmp/typesense:/data \ - --name typesense \ - typesense/typesense:26.0 \ - --api-key=xyz \ - --data-dir /data \ - --enable-cors - - uses: actions/checkout@v4 - - uses: erlef/setup-beam@v1 + docker run -d -p 8108:8108 --name typesense -v/tmp/typesense:/data typesense/typesense:26.0 --api-key=xyz --data-dir /data --enable-cors + + - name: Setup Elixir/OTP + uses: erlef/setup-beam@v1 with: otp-version: '25' elixir-version: '1.14.x' - - uses: actions/cache@v4 + - name: Cache dependencies/builds + uses: actions/cache@v4 with: path: | deps @@ -59,12 +52,6 @@ jobs: restore-keys: | ${{ runner.os }}-mixtest- - # - name: Start Typesense - # uses: jirevwe/typesense-github-action@v1.0.1 - # with: - # typesense-version: ${{ matrix.typesense-version }} - # typesense-api-key: xyz - - name: Install Dependencies run: | mix local.rebar --if-missing From b802be5be687743c1f74e83574ef75a064367682 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Fri, 24 May 2024 00:14:01 +0800 Subject: [PATCH 16/34] update ci.yml --- .github/workflows/ci.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 030a932..ec9a9b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,14 +20,17 @@ jobs: test: runs-on: ubuntu-latest - container: - image: typesense/typesense:26.0 - ports: ['8108:8108'] env: MIX_ENV: test GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + services: + typesense: + image: typesense/typesense:${{ matrix.typesense-version }} + # ports: + # - "8108:8108" + steps: - name: Checkout repo uses: actions/checkout@v4 From 18ab10af9823f314cf46f4d7ffa478a2b6405aeb Mon Sep 17 00:00:00 2001 From: jaeyson Date: Fri, 24 May 2024 00:15:19 +0800 Subject: [PATCH 17/34] add matrix strategy --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec9a9b9..013a03b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,10 @@ jobs: test: runs-on: ubuntu-latest + strategy: + matrix: + typesense-version: [26.0] + env: MIX_ENV: test GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 0f83869c0406bc95a3a3c2833834356362dc168f Mon Sep 17 00:00:00 2001 From: jaeyson Date: Fri, 24 May 2024 00:16:51 +0800 Subject: [PATCH 18/34] version int to string --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 013a03b..d9f4dd1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: - typesense-version: [26.0] + typesense-version: ['26.0'] env: MIX_ENV: test From 6d1fffe959d388a378ebb763e935889cc85c9c59 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Fri, 24 May 2024 00:19:09 +0800 Subject: [PATCH 19/34] disable check format --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9f4dd1..36e2e0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: - name: Checkout repo uses: actions/checkout@v4 - - name: Setup Typesense + - name: Start Typesense run: | docker run -d -p 8108:8108 --name typesense -v/tmp/typesense:/data typesense/typesense:26.0 --api-key=xyz --data-dir /data --enable-cors @@ -80,8 +80,8 @@ jobs: - name: run static analysis run: mix credo --all --strict - - name: check format files - run: mix format --check-formatted + # - name: check format files + # run: mix format --check-formatted - name: Run tests run: mix test From f7c60192b96451e6cc3be92e7d263b575a70f623 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Fri, 24 May 2024 00:30:52 +0800 Subject: [PATCH 20/34] add curl health check --- .github/workflows/ci.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36e2e0f..0bf28d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: - typesense-version: ['26.0'] + typesense-version: ['0.25.2', '26.0'] env: MIX_ENV: test @@ -32,8 +32,6 @@ jobs: services: typesense: image: typesense/typesense:${{ matrix.typesense-version }} - # ports: - # - "8108:8108" steps: - name: Checkout repo @@ -41,7 +39,17 @@ jobs: - name: Start Typesense run: | - docker run -d -p 8108:8108 --name typesense -v/tmp/typesense:/data typesense/typesense:26.0 --api-key=xyz --data-dir /data --enable-cors + docker run -d \ + -p 8108:8108 \ + --name typesense \ + -v /tmp/typesense:/data \ + typesense/typesense:${{ matrix.typesense-version}} \ + --api-key=xyz \ + --data-dir /data \ + --enable-cors + + - name: Curl Typesense + run: sleep 10 && curl http://localhost:8108/health - name: Setup Elixir/OTP uses: erlef/setup-beam@v1 From bc57cb4789fb88775501ee02d4d91c2c0057f585 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Fri, 24 May 2024 01:11:53 +0800 Subject: [PATCH 21/34] update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f57a313..0157d3f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: docker run -d \ -p 8108:8108 \ --name typesense \ - -v /tmp/typesense:/data \ + -v/tmp/typesense:/data \ typesense/typesense:${{ matrix.typesense-version}} \ --api-key=xyz \ --data-dir /data \ From 5cdb3030315c5f81b6b5477af3a3156ce17cace1 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Fri, 24 May 2024 01:13:29 +0800 Subject: [PATCH 22/34] update ci.yml --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0157d3f..edf4e4a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,8 @@ jobs: services: typesense: image: typesense/typesense:${{ matrix.typesense-version }} + ports: + - '8108:8108' options: >- --health-cmd "curl http://localhost:8108/health" --health-interval 10s From dac2e1aa72509756e37e03055dbea69a6177346a Mon Sep 17 00:00:00 2001 From: jaeyson Date: Fri, 24 May 2024 01:15:47 +0800 Subject: [PATCH 23/34] update ci.yml --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index edf4e4a..26b8526 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,10 +32,9 @@ jobs: services: typesense: image: typesense/typesense:${{ matrix.typesense-version }} - ports: - - '8108:8108' options: >- --health-cmd "curl http://localhost:8108/health" + --health-start-period 10s --health-interval 10s --health-timeout 5s --health-retries 5 From 4dd42be934c8532cf3353134c8d39ffc22912361 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Fri, 24 May 2024 01:18:07 +0800 Subject: [PATCH 24/34] update ci.yml --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 26b8526..efb2526 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,12 +32,12 @@ jobs: services: typesense: image: typesense/typesense:${{ matrix.typesense-version }} - options: >- - --health-cmd "curl http://localhost:8108/health" - --health-start-period 10s - --health-interval 10s - --health-timeout 5s - --health-retries 5 + # options: >- + # --health-cmd "curl http://localhost:8108/health" + # --health-start-period 10s + # --health-interval 10s + # --health-timeout 5s + # --health-retries 5 steps: - name: Checkout repo From b86385bca790e904abdcd101e754180bea6a6284 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Fri, 24 May 2024 01:20:44 +0800 Subject: [PATCH 25/34] update ci.yml --- .github/workflows/ci.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index efb2526..ac8955f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,12 +32,6 @@ jobs: services: typesense: image: typesense/typesense:${{ matrix.typesense-version }} - # options: >- - # --health-cmd "curl http://localhost:8108/health" - # --health-start-period 10s - # --health-interval 10s - # --health-timeout 5s - # --health-retries 5 steps: - name: Checkout repo @@ -48,7 +42,7 @@ jobs: docker run -d \ -p 8108:8108 \ --name typesense \ - -v/tmp/typesense:/data \ + -v /tmp/typesense:/data \ typesense/typesense:${{ matrix.typesense-version}} \ --api-key=xyz \ --data-dir /data \ From 75a02d069c2839435817073b7294cc2208f9f53a Mon Sep 17 00:00:00 2001 From: jaeyson Date: Fri, 24 May 2024 01:26:59 +0800 Subject: [PATCH 26/34] add matrix for typesense port --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ac8955f..75518ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,7 @@ jobs: strategy: matrix: typesense-version: ['0.25.2', '26.0'] + typesense-port: ['8108:8108'] env: MIX_ENV: test @@ -40,7 +41,7 @@ jobs: - name: Start Typesense run: | docker run -d \ - -p 8108:8108 \ + -p ${{ matrix.typesense-port }} \ --name typesense \ -v /tmp/typesense:/data \ typesense/typesense:${{ matrix.typesense-version}} \ From e464336dbbe7b4b66b74fe53c5baf364af909041 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Sun, 2 Jun 2024 21:00:49 +0800 Subject: [PATCH 27/34] single compose file for both arch --- README.md | 12 ++++++++++++ linux.yml => docker-compose.yml | 2 +- osx.yml | 10 ---------- start_local.sh | 15 +++++++++++++++ 4 files changed, 28 insertions(+), 11 deletions(-) rename linux.yml => docker-compose.yml (82%) delete mode 100644 osx.yml create mode 100755 start_local.sh diff --git a/README.md b/README.md index 0130ea8..3b7d2fe 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,18 @@ ExTypesense.search(Person, params) Check [Cheatsheet](https://hexdocs.pm/ex_typesense/cheatsheet.html) for more examples. +## Test + +If you want to try/test the package itself, you need Docker for the local typesense instance: + +```bash +# optional, make the script executable +chmod +x start_local.sh + +# run local typesense instance +./start_local.sh +``` + ## License Copyright (c) 2021 Jaeyson Anthony Y. diff --git a/linux.yml b/docker-compose.yml similarity index 82% rename from linux.yml rename to docker-compose.yml index 9e6dab9..ff9b18e 100644 --- a/linux.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: typesense: - image: docker.io/typesense/typesense:26.0 + image: ${TYPESENSE_IMAGE} container_name: typesense restart: on-failure ports: diff --git a/osx.yml b/osx.yml deleted file mode 100644 index a4e7d98..0000000 --- a/osx.yml +++ /dev/null @@ -1,10 +0,0 @@ -services: - typesense: - image: docker.io/typesense/typesense:26.0-arm64 - container_name: typesense - restart: on-failure - ports: - - "8108:8108" - volumes: - - ./typesense-data:/data - command: '--data-dir /data --api-key=xyz --enable-cors' diff --git a/start_local.sh b/start_local.sh new file mode 100755 index 0000000..3c04867 --- /dev/null +++ b/start_local.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +ARCH=$(uname -m) +if [ -z "$ARCH" ]; then + echo "Error: Unable to determine architecture" + exit 1 +fi + +if [ "$ARCH" = "arm64" ]; then + export TYPESENSE_IMAGE=${TYPESENSE_IMAGE:-"docker.io/typesense/typesense:26.0-arm64"} +else + export TYPESENSE_IMAGE=${TYPESENSE_IMAGE:-"docker.io/typesense/typesense:26.0"} +fi + +docker compose up -d && docker container logs --follow --tail 50 typesense From 3cd88199687575d46197d21aee9f27b1c0e92003 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Sun, 2 Jun 2024 22:03:48 +0800 Subject: [PATCH 28/34] source the bash file instead of running with dot --- README.md | 2 +- start_local.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3b7d2fe..7190d25 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,7 @@ If you want to try/test the package itself, you need Docker for the local typese chmod +x start_local.sh # run local typesense instance -./start_local.sh +source start_local.sh ``` ## License diff --git a/start_local.sh b/start_local.sh index 3c04867..5fa9bb4 100755 --- a/start_local.sh +++ b/start_local.sh @@ -12,4 +12,4 @@ else export TYPESENSE_IMAGE=${TYPESENSE_IMAGE:-"docker.io/typesense/typesense:26.0"} fi -docker compose up -d && docker container logs --follow --tail 50 typesense +docker compose up -d From d82b952d79fb7af7e3ce91834ae7842002053df5 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Tue, 4 Jun 2024 15:24:35 +0800 Subject: [PATCH 29/34] remove bash script --- README.md | 12 ------------ docker-compose.yml | 2 +- start_local.sh | 15 --------------- 3 files changed, 1 insertion(+), 28 deletions(-) delete mode 100755 start_local.sh diff --git a/README.md b/README.md index 7190d25..0130ea8 100644 --- a/README.md +++ b/README.md @@ -168,18 +168,6 @@ ExTypesense.search(Person, params) Check [Cheatsheet](https://hexdocs.pm/ex_typesense/cheatsheet.html) for more examples. -## Test - -If you want to try/test the package itself, you need Docker for the local typesense instance: - -```bash -# optional, make the script executable -chmod +x start_local.sh - -# run local typesense instance -source start_local.sh -``` - ## License Copyright (c) 2021 Jaeyson Anthony Y. diff --git a/docker-compose.yml b/docker-compose.yml index ff9b18e..9e6dab9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: typesense: - image: ${TYPESENSE_IMAGE} + image: docker.io/typesense/typesense:26.0 container_name: typesense restart: on-failure ports: diff --git a/start_local.sh b/start_local.sh deleted file mode 100755 index 5fa9bb4..0000000 --- a/start_local.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -ARCH=$(uname -m) -if [ -z "$ARCH" ]; then - echo "Error: Unable to determine architecture" - exit 1 -fi - -if [ "$ARCH" = "arm64" ]; then - export TYPESENSE_IMAGE=${TYPESENSE_IMAGE:-"docker.io/typesense/typesense:26.0-arm64"} -else - export TYPESENSE_IMAGE=${TYPESENSE_IMAGE:-"docker.io/typesense/typesense:26.0"} -fi - -docker compose up -d From 49d32361b70fb8e768f39ed6fc2da194c7ae77bc Mon Sep 17 00:00:00 2001 From: jaeyson Date: Sun, 9 Jun 2024 01:07:57 +0800 Subject: [PATCH 30/34] update docs --- .github/workflows/ci.yml | 8 ++--- CHANGELOG.md | 3 +- README.md | 69 ++++++++++++++++++++++++++++++++++++---- mix.exs | 2 +- mix.lock | 6 ++-- 5 files changed, 73 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a6b4d35..a732821 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,9 +64,9 @@ jobs: path: | deps _build - key: ${{ runner.os }}-mixtest-${{ hashFiles('**/mix.lock') }} + key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} restore-keys: | - ${{ runner.os }}-mixtest- + ${{ runner.os }}-mix- - name: Install Dependencies run: | @@ -89,8 +89,8 @@ jobs: - name: run static analysis run: mix credo --all --strict - # - name: check format files - # run: mix format --check-formatted + - name: check format files + run: mix format --check-formatted - name: Run tests run: mix test diff --git a/CHANGELOG.md b/CHANGELOG.md index e83945b..3d8432d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,10 +16,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Refactor `ExTypesense.HttpClient` on how to handle request. * Bumped dependencies' version. +* Dropped usage of `:httpc` in favor of using [`Req library`](https://hex.pm/packages/req). ### Deprecated -* Some functions from `Cluster`, `Collection`, `Document`, `HttpClient` and `Search` where soft depcrated in order to incorporate the `Connection` module for dynamic connections loaded from Ecto schema. If you read the docs, you might notice it's marked with `deprecated` and encourages to use the newer ones. +* Some functions from `Document` and `HttpClient` where soft depcrated in order to incorporate the `Connection` module for dynamic connections loaded from Ecto schema. If you read the docs, you might notice it's marked with `deprecated` and encourages to use the newer ones. ## 0.3.5 (2023.08.13) diff --git a/README.md b/README.md index 0130ea8..f11e960 100644 --- a/README.md +++ b/README.md @@ -38,11 +38,7 @@ end ## Getting started -### 1. Add credential to config - -After you have setup a [local](./guides/running_local_typesense.md) Typesense or [Cloud hosted](https://cloud.typesense.org) instance, you can set the following config details to the config file: - -#### Run local Typesense instance +### 0. Run local Typesense instance ```bash # Linux @@ -54,7 +50,11 @@ docker compose -f osx.yml up -d More info on spinning a local instance: https://typesense.org/docs/guide/install-typesense -#### Set credentials via config (e.g. `config/runtime.exs`) +### 1. Add credential to config + +After you have setup a [local](./guides/running_local_typesense.md) Typesense or [Cloud hosted](https://cloud.typesense.org) instance, you can set the following config details to the config file: + +#### (Option 1) Set credentials via config (e.g. `config/runtime.exs`) ```elixir config :ex_typesense, @@ -74,6 +74,63 @@ config :ex_typesense, scheme: "https" ``` +#### (Option 2) Dynamic connection using an Ecto schema + +> By default you don't need to pass connections every +> time you use a function, if you use "Option 1" above. + +You may have a `Connection` Ecto schema in your app and want to pass your own creds dynamically. + +```elixir +defmodule MyApp.Credential do + schema "credentials" do + field :node, :string + field :secret_key, :string + field :port, :integer + end +end +``` + +```elixir +credential = MyApp.Credential |> where(id: ^8888) |> Repo.one() + +# using Connection struct +conn = %ExTypesense.Connection{ + host: credential.node, + api_key: credential.secret_key, + port: credential.port, + scheme: "https" +} + +# or maps, as long as the keys matches in ExTypesense.Connection.t() +conn = %{ + host: credential.node, + api_key: credential.secret_key, + port: credential.port, + scheme: "https" +} + +# or convert your struct to map, as long as the keys matches in ExTypesense.Connection.t() +conn = Map.from_struct(MyApp.Credential) + +# or you don't want to change the fields in your schema, thus you convert it to map +conn = %Credential{ + node: "localhost", + secret_key: "xyz", + port: 8108, + scheme: "http" +} + +conn = + conn + |> Map.from_struct() + |> Map.drop([:node, :secret_key]) + |> Map.put(:host, conn.node) + |> Map.put(:api_key, conn.secret_key) + +ExTypesense.search(conn, collection_name, query) +``` + ### 2. Create a collection There are 2 ways to create a collection, either via [Ecto schema](https://hexdocs.pm/ecto/Ecto.Schema.html) or using map ([an Elixir data type](https://hexdocs.pm/elixir/keywords-and-maps.html#maps-as-key-value-pairs)): diff --git a/mix.exs b/mix.exs index 3d8bd59..44d755d 100644 --- a/mix.exs +++ b/mix.exs @@ -38,7 +38,7 @@ defmodule ExTypesense.MixProject do [ {:ex_doc, "~> 0.32", only: [:dev, :test], runtime: false}, {:credo, "~> 1.7.0", only: [:dev, :test], runtime: false}, - {:req, "~> 0.4.14"}, + {:req, "~> 0.5"}, {:ecto, "~> 3.11"}, {:excoveralls, "~> 0.10", only: :test}, {:mix_audit, "~> 2.1", only: [:dev, :test], runtime: false} diff --git a/mix.lock b/mix.lock index f6831f6..430d20f 100644 --- a/mix.lock +++ b/mix.lock @@ -5,7 +5,7 @@ "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, "ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"}, - "ex_doc": {:hex, :ex_doc, "0.33.0", "690562b153153c7e4d455dc21dab86e445f66ceba718defe64b0ef6f0bd83ba0", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "3f69adc28274cb51be37d09b03e4565232862a4b10288a3894587b0131412124"}, + "ex_doc": {:hex, :ex_doc, "0.34.0", "ab95e0775db3df71d30cf8d78728dd9261c355c81382bcd4cefdc74610bef13e", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "60734fb4c1353f270c3286df4a0d51e65a2c1d9fba66af3940847cc65a8066d7"}, "excoveralls": {:hex, :excoveralls, "0.18.1", "a6f547570c6b24ec13f122a5634833a063aec49218f6fff27de9df693a15588c", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d65f79db146bb20399f23046015974de0079668b9abb2f5aac074d078da60b8d"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"}, @@ -17,11 +17,11 @@ "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, "mint": {:hex, :mint, "1.6.0", "88a4f91cd690508a04ff1c3e28952f322528934be541844d54e0ceb765f01d5e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "3c5ae85d90a5aca0a49c0d8b67360bbe407f3b54f1030a111047ff988e8fefaa"}, "mix_audit": {:hex, :mix_audit, "2.1.3", "c70983d5cab5dca923f9a6efe559abfb4ec3f8e87762f02bab00fa4106d17eda", [:make, :mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.9", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "8c3987100b23099aea2f2df0af4d296701efd031affb08d0746b2be9e35988ec"}, - "nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"}, + "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_ownership": {:hex, :nimble_ownership, "0.3.1", "99d5244672fafdfac89bfad3d3ab8f0d367603ce1dc4855f86a1c75008bce56f", [:mix], [], "hexpm", "4bf510adedff0449a1d6e200e43e57a814794c8b5b6439071274d248d272a549"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, - "req": {:hex, :req, "0.4.14", "103de133a076a31044e5458e0f850d5681eef23dfabf3ea34af63212e3b902e2", [:mix], [{:aws_signature, "~> 0.3.2", [hex: :aws_signature, repo: "hexpm", optional: true]}, {:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:nimble_ownership, "~> 0.2.0 or ~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "2ddd3d33f9ab714ced8d3c15fd03db40c14dbf129003c4a3eb80fac2cc0b1b08"}, + "req": {:hex, :req, "0.5.0", "6d8a77c25cfc03e06a439fb12ffb51beade53e3fe0e2c5e362899a18b50298b3", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "dda04878c1396eebbfdec6db6f3d4ca609e5c8846b7ee88cc56eb9891406f7a3"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, "yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"}, "yaml_elixir": {:hex, :yaml_elixir, "2.9.0", "9a256da867b37b8d2c1ffd5d9de373a4fda77a32a45b452f1708508ba7bbcb53", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "0cb0e7d4c56f5e99a6253ed1a670ed0e39c13fc45a6da054033928607ac08dfc"}, From da8061d70db2e1ba516461e79ba69fc8251219fd Mon Sep 17 00:00:00 2001 From: jaeyson Date: Sun, 9 Jun 2024 01:13:22 +0800 Subject: [PATCH 31/34] update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a732821..415892a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: -v /tmp/typesense:/data \ typesense/typesense:${{ matrix.typesense-version}} \ --api-key=xyz \ - --data-dir /data \ + --data-dir=/data \ --enable-cors - name: Curl Typesense From a421f5e1cb94dcf5be7d70e673a936ebe031f1ca Mon Sep 17 00:00:00 2001 From: jaeyson Date: Sun, 9 Jun 2024 01:15:52 +0800 Subject: [PATCH 32/34] unlock mix deps unused --- mix.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/mix.lock b/mix.lock index 430d20f..cfefede 100644 --- a/mix.lock +++ b/mix.lock @@ -18,7 +18,6 @@ "mint": {:hex, :mint, "1.6.0", "88a4f91cd690508a04ff1c3e28952f322528934be541844d54e0ceb765f01d5e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "3c5ae85d90a5aca0a49c0d8b67360bbe407f3b54f1030a111047ff988e8fefaa"}, "mix_audit": {:hex, :mix_audit, "2.1.3", "c70983d5cab5dca923f9a6efe559abfb4ec3f8e87762f02bab00fa4106d17eda", [:make, :mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.9", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "8c3987100b23099aea2f2df0af4d296701efd031affb08d0746b2be9e35988ec"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, - "nimble_ownership": {:hex, :nimble_ownership, "0.3.1", "99d5244672fafdfac89bfad3d3ab8f0d367603ce1dc4855f86a1c75008bce56f", [:mix], [], "hexpm", "4bf510adedff0449a1d6e200e43e57a814794c8b5b6439071274d248d272a549"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "req": {:hex, :req, "0.5.0", "6d8a77c25cfc03e06a439fb12ffb51beade53e3fe0e2c5e362899a18b50298b3", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "dda04878c1396eebbfdec6db6f3d4ca609e5c8846b7ee88cc56eb9891406f7a3"}, From 4a5403d0b3769270838909539f0b3692ee509e9f Mon Sep 17 00:00:00 2001 From: jaeyson Date: Sun, 9 Jun 2024 01:22:42 +0800 Subject: [PATCH 33/34] update ci.yml --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 415892a..44da006 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,10 +43,10 @@ jobs: docker run -d \ -p ${{ matrix.typesense-port }} \ --name typesense \ - -v /tmp/typesense:/data \ + -v /tmp/typesense-data:/data \ typesense/typesense:${{ matrix.typesense-version}} \ --api-key=xyz \ - --data-dir=/data \ + --data-dir /data \ --enable-cors - name: Curl Typesense From a7392065fd86577c55e0efd85f6d579e66a66566 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Sun, 9 Jun 2024 10:54:11 +0800 Subject: [PATCH 34/34] update ci.yml --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44da006..b9d4752 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,8 +89,8 @@ jobs: - name: run static analysis run: mix credo --all --strict - - name: check format files - run: mix format --check-formatted + # - name: check format files + # run: mix format --check-formatted - name: Run tests run: mix test