Skip to content

Commit

Permalink
feat: missing transform builder functions (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
zoedsoupe authored Dec 30, 2024
1 parent 646fab7 commit e22c90b
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 0 deletions.
143 changes: 143 additions & 0 deletions lib/supabase/postgrest/transform_builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,147 @@ defmodule Supabase.PostgREST.TransformBuilder do
def maybe_single(%Builder{} = b) do
Supabase.PostgREST.with_custom_media_type(b, :pgrst_object)
end

@doc """
Return `data` as a string in CSV format.
## Examples
iex> PostgREST.csv(builder)
%Builder{headers: %{"accept" => "text/csv"}}
## See also
https://supabase.com/docs/reference/javascript/db-csv
"""
@impl true
def csv(%Builder{} = b) do
Builder.add_request_header(b, "accept", "text/csv")
end

@doc """
Return `data` as an object in [GeoJSON](https://geojson.org) format.
## Examples
iex> PostgREST.csv(builder)
%Builder{headers: %{"accept" => "application/geo+json"}}
"""
@impl true
def geojson(%Builder{} = b) do
Builder.add_request_header(b, "accept", "application/geo+json")
end

@explain_default [
analyze: false,
verbose: false,
settings: false,
buffers: false,
wal: false
]

@doc """
Return `data` as the EXPLAIN plan for the query.
You need to enable the [db_plan_enabled](https://supabase.com/docs/guides/database/debugging-performance#enabling-explain) setting before using this method.
## Params
- `options`: options as a keyword list, these are the possibilities:
- `analyze`: boolean, defaults to `false`
- `verbose`: boolean, defaults to `false`
- `settings`: boolean, default to `false`
- `buffers`: boolean, defaults to `false`
- `wal`: boolean, default to `false`
- `format`: `:json` or `:text`, defaults to `:text`
## Examples
iex> PostgREST.explain(builder, analyze: true, format: :json, wal: false)
%Builder{}
## See also
https://supabase.com/docs/reference/javascript/explain
"""
@impl true
def explain(%Builder{} = b, opts \\ []) do
format =
opts
|> Keyword.get(:format, :text)
|> then(fn format ->
if format in [:json, :text] do
"+#{format};"
else
"+text;"
end
end)

opts =
@explain_default
|> Keyword.merge(opts)
|> Enum.filter(&elem(&1, 1))
|> Enum.map_join("|", &elem(&1, 0))
|> then(&"options:#{&1}")

# postgrest-ex sends always only one Accept header
# and always sets a default (application/json)
for_mediatype = "for=#{b.headers["accept"]}"

plan = "application/vnd.pgrst.plan#{format};#{for_mediatype};#{opts}"

Builder.add_request_header(b, "accept", plan)
end

@doc """
Rollback the query. `data` will still be returned, but the query is not committed.
## Examples
iex> PostgREST.rollback(builder)
%Builder{headers: %{"prefer" => "tx=rollback"}}
"""
@impl true
def rollback(%Builder{} = b) do
if prefer = b.headers["prefer"] do
Builder.add_request_header(b, "prefer", "#{prefer},tx=rollback")
else
Builder.add_request_header(b, "prefer", "tx=rollback")
end
end

@doc """
Perform a SELECT on the query result.
By default, `.insert()`, `.update()`, `.upsert()`, and `.delete()` do not
return modified rows. By calling this method, modified rows are returned in
`data`.
**Do not** confuse with the `Supabase.PostgREST.QueryBuilder.select/3` function.
## Params
If called without additional arguments (besides builder), it will fallback to select all
relation (table) columns (with `"*"`), otherwise you can pass a list of strings representing
the columns to be selected
## Examples
iex> PostgREST.insert(builder, %{foo: :bar}) |> PostgREST.returning(~w(id foo))
## See also
https://supabase.com/docs/reference/javascript/db-modifiers-select
"""
@impl true
def returning(%Builder{} = b) do
prefer = if p = b.headers["prefer"], do: p <> ",", else: ""

b
|> Builder.add_query_param("select", "*")
|> Builder.add_request_header("prefer", "#{prefer}return=representation")
end

@impl true
def returning(%Builder{} = b, columns) when is_list(columns) do
cols =
Enum.map_join(columns, ",", fn c ->
if String.match?(c, ~r/\"/), do: c, else: String.trim(c)
end)

prefer = if p = b.headers["prefer"], do: p <> ",", else: ""

b
|> Builder.add_query_param("select", cols)
|> Builder.add_request_header("prefer", "#{prefer}return=representation")
end
end
8 changes: 8 additions & 0 deletions lib/supabase/postgrest/transform_builder/behaviour.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,12 @@ defmodule Supabase.PostgREST.TransformBuilder.Behaviour do
@callback range(Builder.t(), from :: integer, to :: integer) :: Builder.t()
@callback range(Builder.t(), from :: integer, to :: integer, foreign_table: String.t()) ::
Builder.t()
@callback rollback(Builder.t()) :: Builder.t()
@callback returning(Builder.t()) :: Builder.t()
@callback returning(Builder.t(), list(String.t()) | String.t()) :: Builder.t()
@callback csv(Builder.t()) :: Builder.t()
@callback geojson(Builder.t()) :: Builder.t()
@callback explain(Builder.t(), options :: explain) :: Builder.t()
when explain: list({opt, boolean} | {:format, :json | :text}),
opt: :analyze | :verbose | :settings | :buffers | :wal
end

0 comments on commit e22c90b

Please sign in to comment.