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

nest adapter opts #379

Merged
merged 2 commits into from
Jul 16, 2023
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
13 changes: 11 additions & 2 deletions lib/flop.ex
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,15 @@ defmodule Flop do
__CALLER__.module
)

opts = Keyword.put(opts, :adapter, Flop.Adapter.Ecto)
{legacy_adapter_opts, opts} = Keyword.split(opts, [:query_opts, :repo])
adapter_schema = Keyword.fetch!(opts, :adapter).backend_options()

adapter_opts =
legacy_adapter_opts
|> Keyword.merge(Keyword.fetch!(opts, :adapter_opts))
|> NimbleSchemas.validate!(adapter_schema, Flop.Schema, __CALLER__.module)

opts = Keyword.put(opts, :adapter_opts, adapter_opts)

quote do
@doc false
Expand Down Expand Up @@ -455,7 +463,8 @@ defmodule Flop do
| {:extra_opts, Keyword.t()}
| private_option()

@typep private_option :: {:adapter, module} | {:backend, module}
@typep private_option ::
{:adapter, module} | {:adapter_opts, keyword} | {:backend, module}

@type default_order ::
%{
Expand Down
3 changes: 3 additions & 0 deletions lib/flop/adapter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,7 @@ defmodule Flop.Adapter do
@callback count(queryable, opts) :: non_neg_integer

@callback list(queryable, opts) :: [any]

@callback backend_options() :: NimbleOptions.t()
@callback schema_options() :: NimbleOptions.t()
end
66 changes: 66 additions & 0 deletions lib/flop/adapter/ecto.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,69 @@ defmodule Flop.Adapter.Ecto do
:ilike_or
]

@backend_options [
repo: [required: true],
query_opts: [type: :keyword_list, default: []]
]

@schema_options [
join_fields: [
type: :keyword_list,
keys: [
*: [
type:
{:or,
[
keyword_list: [
binding: [type: :atom, required: true],
field: [type: :atom, required: true],
ecto_type: [type: :any],
path: [type: {:list, :atom}]
],
tuple: [:atom, :atom]
]}
]
]
],
compound_fields: [
type: :keyword_list,
keys: [
*: [
type: {:list, :atom}
]
]
],
custom_fields: [
type: :keyword_list,
keys: [
*: [
type: :keyword_list,
keys: [
filter: [
type: {:tuple, [:atom, :atom, :keyword_list]},
required: true
],
ecto_type: [type: :any],
bindings: [type: {:list, :atom}],
operators: [type: {:list, :atom}]
]
]
]
],
alias_fields: [
type: {:list, :atom}
]
]

@backend_options NimbleOptions.new!(@backend_options)
@schema_options NimbleOptions.new!(@schema_options)

@impl Flop.Adapter
def backend_options, do: @backend_options

@impl Flop.Adapter
def schema_options, do: @schema_options

@impl Flop.Adapter
def apply_filter(
query,
Expand Down Expand Up @@ -185,6 +248,9 @@ defmodule Flop.Adapter.Ecto do
end

defp apply_on_repo(repo_fn, flop_fn, args, opts) do
# use nested adapter_opts if set
opts = Flop.get_option(:adapter_opts, opts) || opts

repo = Flop.get_option(:repo, opts) || raise no_repo_error(flop_fn)
opts = query_opts(opts)

Expand Down
23 changes: 20 additions & 3 deletions lib/flop/nimble_schemas.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ defmodule Flop.NimbleSchemas do
@moduledoc false

@backend_option [
adapter: [type: :atom, default: Flop.Adapter.Ecto],
adapter_opts: [
type: :keyword_list,
default: []
],
cursor_value_func: [type: {:fun, 2}],
default_limit: [type: :integer, default: 50],
max_limit: [type: :integer, default: 1000],
Expand All @@ -26,10 +31,15 @@ defmodule Flop.NimbleSchemas do
default: [:offset, :page, :first, :last]
],
repo: [],
query_opts: [type: :keyword_list]
query_opts: [type: :keyword_list, default: []]
]

@schema_option [
adapter: [type: :atom, default: Flop.Adapter.Ecto],
adapter_opts: [
type: :keyword_list,
default: []
],
filterable: [type: {:list, :atom}, required: true],
sortable: [type: {:list, :atom}, required: true],
default_order: [
Expand Down Expand Up @@ -107,11 +117,18 @@ defmodule Flop.NimbleSchemas do
]
]

@schema_option_schema @schema_option
def schema_option_schema, do: @schema_option_schema

@backend_option NimbleOptions.new!(@backend_option)
@schema_option NimbleOptions.new!(@schema_option)

def validate!(opts, schema_id, module, caller) do
case NimbleOptions.validate(opts, schema(schema_id)) do
def validate!(opts, schema_id, module, caller) when is_atom(schema_id) do
validate!(opts, schema(schema_id), module, caller)
end

def validate!(opts, %NimbleOptions{} = schema, module, caller) do
case NimbleOptions.validate(opts, schema) do
{:ok, opts} ->
opts

Expand Down
39 changes: 27 additions & 12 deletions lib/flop/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,22 @@ defimpl Flop.Schema, for: Any do
__CALLER__.module
)

validate_options!(options, struct)
legacy_adapter_opts =
Keyword.take(options, [
:alias_fields,
:compound_fields,
:custom_fields,
:join_fields
])

adapter_schema = Keyword.fetch!(options, :adapter).schema_options()

adapter_opts =
legacy_adapter_opts
|> Keyword.merge(Keyword.fetch!(options, :adapter_opts))
|> NimbleSchemas.validate!(adapter_schema, Flop.Schema, __CALLER__.module)

validate_options!(options, adapter_opts, struct)

filterable_fields = Keyword.get(options, :filterable)
sortable_fields = Keyword.get(options, :sortable)
Expand All @@ -811,16 +826,16 @@ defimpl Flop.Schema, for: Any do
pagination_types = Keyword.get(options, :pagination_types)
default_pagination_type = Keyword.get(options, :default_pagination_type)
default_order = Keyword.get(options, :default_order)
compound_fields = Keyword.get(options, :compound_fields, [])
alias_fields = Keyword.get(options, :alias_fields, [])
compound_fields = Keyword.get(adapter_opts, :compound_fields, [])
alias_fields = Keyword.get(adapter_opts, :alias_fields, [])

custom_fields =
options
adapter_opts
|> Keyword.get(:custom_fields, [])
|> Enum.map(&normalize_custom_opts/1)

join_fields =
options
adapter_opts
|> Keyword.get(:join_fields, [])
|> Enum.map(&normalize_join_opts/1)

Expand Down Expand Up @@ -894,12 +909,12 @@ defimpl Flop.Schema, for: Any do
end
end

defp validate_options!(opts, struct) do
compound_fields = get_compound_fields(opts)
join_fields = get_join_fields(opts)
defp validate_options!(opts, adapter_opts, struct) do
compound_fields = get_compound_fields(adapter_opts)
join_fields = get_join_fields(adapter_opts)
schema_fields = get_schema_fields(struct)
alias_fields = Keyword.get(opts, :alias_fields, [])
custom_fields = get_custom_fields(opts)
alias_fields = Keyword.get(adapter_opts, :alias_fields, [])
custom_fields = get_custom_fields(adapter_opts)

all_fields =
compound_fields ++
Expand All @@ -917,9 +932,9 @@ defimpl Flop.Schema, for: Any do
validate_no_unknown_field!(opts[:filterable], all_fields, "filterable")
validate_no_unknown_field!(opts[:sortable], all_fields, "sortable")
validate_default_order!(opts[:default_order], opts[:sortable])
validate_compound_fields!(opts[:compound_fields], all_fields)
validate_compound_fields!(adapter_opts[:compound_fields], all_fields)
validate_alias_fields!(alias_fields, opts[:filterable])
validate_custom_fields!(opts[:custom_fields], opts[:sortable])
validate_custom_fields!(adapter_opts[:custom_fields], opts[:sortable])
end

defp get_compound_fields(opts) do
Expand Down
29 changes: 29 additions & 0 deletions test/flop_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ defmodule FlopTest do
use Flop, repo: Flop.Repo, default_limit: 35
end

defmodule TestProviderNested do
use Flop,
adapter_opts: [repo: Flop.Repo],
default_limit: 35
end

describe "ordering" do
test "adds order_by to query if set" do
pets = insert_list(20, :pet)
Expand Down Expand Up @@ -1961,6 +1967,29 @@ defmodule FlopTest do
end
end

describe "__using__/1 with nested adapter options" do
test "defines wrapper functions that pass default options" do
insert_list(3, :pet)

assert {:ok, {_, %Meta{page_size: 35}}} =
TestProviderNested.validate_and_run(Pet, %{})
end

test "allows to override defaults" do
insert_list(3, :pet)

assert {:ok, {_, %Meta{page_size: 30}}} =
TestProviderNested.validate_and_run(Pet, %{page_size: 30})
end

test "passes backend module" do
assert {:ok, {_, %Meta{backend: TestProviderNested, opts: opts}}} =
TestProviderNested.validate_and_run(Pet, %{})

assert Keyword.get(opts, :backend) == TestProviderNested
end
end

describe "get_option/3" do
test "returns value from option list" do
# sanity check
Expand Down
46 changes: 26 additions & 20 deletions test/support/pet.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,34 @@ defmodule MyApp.Pet do
],
sortable: [:name, :age, :owner_name, :owner_age],
max_limit: 1000,
compound_fields: [
full_name: [:family_name, :given_name],
pet_and_owner_name: [:name, :owner_name]
],
join_fields: [
owner_age: {:owner, :age},
owner_name: [
binding: :owner,
field: :name,
path: [:owner, :name],
ecto_type: :string
adapter_opts: [
compound_fields: [
full_name: [:family_name, :given_name],
pet_and_owner_name: [:name, :owner_name]
],
owner_tags: [binding: :owner, field: :tags, ecto_type: {:array, :string}]
],
custom_fields: [
custom: [
filter: {__MODULE__, :test_custom_filter, [some: :options]},
operators: [:==]
join_fields: [
owner_age: {:owner, :age},
owner_name: [
binding: :owner,
field: :name,
path: [:owner, :name],
ecto_type: :string
],
owner_tags: [
binding: :owner,
field: :tags,
ecto_type: {:array, :string}
]
],
reverse_name: [
filter: {__MODULE__, :reverse_name_filter, []},
ecto_type: :string
custom_fields: [
custom: [
filter: {__MODULE__, :test_custom_filter, [some: :options]},
operators: [:==]
],
reverse_name: [
filter: {__MODULE__, :reverse_name_filter, []},
ecto_type: :string
]
]
]
}
Expand Down