Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Batch Document Get #3

Merged
merged 3 commits into from
Aug 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/firestore.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ defmodule Firestore do

@type t :: %__MODULE__{
config: map(),
client: Connection.t()
client: Firestore.Connection.t()
}
end

Expand Down
4 changes: 4 additions & 0 deletions lib/firestore/api.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ defmodule Firestore.API do
to: Projects,
as: :firestore_projects_databases_documents_get

defdelegate batch_get_documents(conn, path, params \\ [], opts \\ []),
to: Projects,
as: :firestore_projects_databases_documents_batch_get

defdelegate create_document(conn, parent, collection_id, params \\ [], opts \\ []),
to: Projects,
as: :firestore_projects_databases_documents_create_document
Expand Down
3 changes: 1 addition & 2 deletions lib/firestore/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ defmodule Firestore.Connection do
Tesla.client(middleware, http_adapter)
end

defp build_http_adapter(%{otp_app: _, tesla_adapter: :finch,name: name}),
defp build_http_adapter(%{otp_app: _, tesla_adapter: :finch, name: name}),
do: {@http_adapters[:finch], [name: name]}

defp build_http_adapter(%{otp_app: _, tesla_adapter: adapter, pool_size: nil}),
Expand All @@ -48,7 +48,6 @@ defmodule Firestore.Connection do
defp build_http_adapter(%{otp_app: _, tesla_adapter: :ibrowse, pool_size: pool_size}),
do: {@http_adapters[:ibrowse], [max_sessions: pool_size, max_pipeline_size: 1]}


defp build_http_adapter(%{tesla_adapter: adapter} = config) do
Logger.warning("Ignoring pool_size option as #{adapter} does not support it")

Expand Down
11 changes: 11 additions & 0 deletions lib/firestore/decoder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ defimpl Firestore.Decoder, for: GoogleApi.Firestore.V1.Model.Empty do
def decode(_response), do: {:ok, []}
end

defimpl Firestore.Decoder, for: GoogleApi.Firestore.V1.Model.BatchGetDocumentsResponse do
def decode(%{found: document, missing: nil}) do
{:ok, decoded_document} = Firestore.Decoder.decode(document)
%{path: document.name, document: decoded_document}
end

def decode(%{found: nil, missing: missing}) do
%{path: missing, document: nil}
end
end

defimpl Firestore.Decoder, for: GoogleApi.Firestore.V1.Model.ListDocumentsResponse do
def decode(%{documents: docs, nextPageToken: next}) do
response = %{documents: Enum.map(docs, &decode/1), nextPageToken: next}
Expand Down
55 changes: 40 additions & 15 deletions lib/firestore/repo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ defmodule Firestore.Repo do
"""
@callback get(String.t(), Keyword.t()) :: {:ok, map()} | {:error, term()}

@doc """
Returns a map of documents for given document paths. Returns `nil` if no result was found.
"""
@callback batch_get_documents([String.t()], map()) :: {:ok, map()} | {:error, term()}

@doc """
Creates a document in a collection given a path.
"""
Expand All @@ -84,54 +89,73 @@ defmodule Firestore.Repo do
@tesla_adapter opts[:tesla_adapter]
@pool_size opts[:pool_size]
@read_only opts[:read_only]
#Used for Finch tesla adapter
# Used for Finch tesla adapter
@name opts[:name]

def config() do
@otp_app
|> Application.get_env(__MODULE__, [])
|> Keyword.merge(otp_app: @otp_app, tesla_adapter: @tesla_adapter, pool_size: @pool_size, name: @name)
|> Keyword.merge(
otp_app: @otp_app,
tesla_adapter: @tesla_adapter,
pool_size: @pool_size,
name: @name
)
|> Map.new()
end

def get(path, opts \\ []) do
def get(path, params \\ []) do
with {:ok, client} <- get_client(),
{:ok, response} <-
Firestore.API.get_document(client, build_document_path(path), params) do
Firestore.Decoder.decode(response)
end
end

def batch_get_documents(document_paths, params \\ %{}) do
params =
params
|> Map.put(:documents, Enum.map(document_paths, &build_document_path/1))
|> then(fn params -> Keyword.put([], :body, params) end)

with {:ok, client} <- get_client(),
{:ok, response} <- Firestore.API.get_document(client, build_path(path), opts) do
{:ok, response} <- Firestore.API.batch_get_documents(client, db_path(), params) do
Firestore.Decoder.decode(response)
end
end

unless @read_only do
def insert(collection_id, parent, payload, opts \\ []) do
def insert(collection_id, parent, payload, params \\ []) do
with {:ok, client} <- get_client(),
{:ok, response} <-
Firestore.API.create_document(
client,
build_path(parent),
build_document_path(parent),
collection_id,
Keyword.put(opts, :body, Firestore.Encoder.encode(payload))
Keyword.put(params, :body, Firestore.Encoder.encode(payload))
) do
Firestore.Decoder.decode(response)
end
end

def update(document_path, payload, opts \\ []) do
def update(document_path, payload, params \\ []) do
with {:ok, client} <- get_client(),
{:ok, response} <-
Firestore.API.update_document(
client,
build_path(document_path),
Keyword.put([], :body, Firestore.Encoder.encode(payload)) |> should_mask?(opts)
build_document_path(document_path),
Keyword.put([], :body, Firestore.Encoder.encode(payload))
|> should_mask?(params)
) do
Firestore.Decoder.decode(response)
end
end
end

defp should_mask?(keyword, opts) when opts == [], do: keyword
defp should_mask?(keyword, params) when params == [], do: keyword

defp should_mask?(keyword, opts),
do: Keyword.put(keyword, :"updateMask.fieldPaths", opts[:fields])
defp should_mask?(keyword, params),
do: Keyword.put(keyword, :"updateMask.fieldPaths", params[:fields])

defp get_client() do
case :ets.lookup(:firestore_table, :"#{@otp_app}_firestore_client") do
Expand All @@ -144,12 +168,13 @@ defmodule Firestore.Repo do
end
end

defp build_path(path) do
defp db_path() do
@otp_app
|> Application.get_env(__MODULE__, [])
|> Keyword.get(:url)
|> then(fn db_url -> "#{db_url}/#{path}" end)
end

defp build_document_path(path), do: "#{db_path()}/documents/#{path}"
end
end

Expand Down
Loading