|
| 1 | +defmodule Dx.Repo do |
| 2 | + @moduledoc """ |
| 3 | + Defines a repository with default options, similar to `Ecto.Repo`. |
| 4 | +
|
| 5 | + When used, the repository expects the `:otp_app` as option. |
| 6 | + The `:otp_app` should point to an OTP application that has |
| 7 | + the repository configuration. For example, the repository: |
| 8 | +
|
| 9 | + defmodule Repo do |
| 10 | + use Dx.Repo, |
| 11 | + otp_app: :my_app, |
| 12 | + loader: Dx.Loaders.Dataloader, |
| 13 | + loader_options: [telemetry_options: [dx: true]] |
| 14 | + end |
| 15 | +
|
| 16 | + Could be configured with: |
| 17 | +
|
| 18 | + config :my_app, Repo, |
| 19 | + loader_options: [timeout: 20_000] |
| 20 | +
|
| 21 | + See `Dx` for further options. The options are deep-merged, |
| 22 | + with the following order of precedence: |
| 23 | +
|
| 24 | + 1. Options passed to boundary functions, such as `Dx.get/3` |
| 25 | + 2. Options returned by `Dx.Repo.default_options/1` callback |
| 26 | + 3. Options from config |
| 27 | + 4. Options from `use Dx.Repo, ...`. |
| 28 | + """ |
| 29 | + |
| 30 | + @doc false |
| 31 | + defmacro __using__(opts) do |
| 32 | + quote bind_quoted: [opts: opts] do |
| 33 | + @behaviour Dx.Repo |
| 34 | + @otp_app Keyword.fetch!(opts, :otp_app) |
| 35 | + @opts opts |
| 36 | + |
| 37 | + def default_options(_operation), do: [] |
| 38 | + defoverridable default_options: 1 |
| 39 | + |
| 40 | + @compile {:inline, prepare_opts: 2} |
| 41 | + defp prepare_opts(operation_name, opts) do |
| 42 | + config = Application.get_env(otp_app, __MODULE__, []) |
| 43 | + |
| 44 | + @opts |
| 45 | + |> Dx.Util.Keyword.deep_merge(config) |
| 46 | + |> Dx.Util.Keyword.deep_merge(default_options(operation_name)) |
| 47 | + |> Dx.Util.Keyword.deep_merge(opts) |
| 48 | + end |
| 49 | + |
| 50 | + def get(records, predicates, opts \\ []) do |
| 51 | + Dx.get(records, predicates, prepare_opts(opts, :get)) |
| 52 | + end |
| 53 | + |
| 54 | + def get!(records, predicates, opts \\ []) do |
| 55 | + Dx.get!(records, predicates, prepare_opts(opts, :get)) |
| 56 | + end |
| 57 | + |
| 58 | + def load(records, predicates, opts \\ []) do |
| 59 | + Dx.load(records, predicates, prepare_opts(opts, :load)) |
| 60 | + end |
| 61 | + |
| 62 | + def load!(records, predicates, opts \\ []) do |
| 63 | + Dx.load!(records, predicates, prepare_opts(opts, :load)) |
| 64 | + end |
| 65 | + |
| 66 | + def put(records, predicates, opts \\ []) do |
| 67 | + Dx.put(records, predicates, prepare_opts(opts, :put)) |
| 68 | + end |
| 69 | + |
| 70 | + def put!(records, predicates, opts \\ []) do |
| 71 | + Dx.put!(records, predicates, prepare_opts(opts, :put)) |
| 72 | + end |
| 73 | + |
| 74 | + def filter(records, condition, opts \\ []) when is_list(records) do |
| 75 | + Dx.filter(records, condition, prepare_opts(opts, :filter)) |
| 76 | + end |
| 77 | + |
| 78 | + def reject(records, condition, opts \\ []) when is_list(records) do |
| 79 | + Dx.reject(records, condition, prepare_opts(opts, :reject)) |
| 80 | + end |
| 81 | + |
| 82 | + def query_all(queryable, condition, opts \\ []) do |
| 83 | + Dx.query_all(queryable, condition, prepare_opts(opts, :query_all)) |
| 84 | + end |
| 85 | + |
| 86 | + def query_one(queryable, condition, opts \\ []) do |
| 87 | + Dx.query_one(queryable, condition, prepare_opts(opts, :query_one)) |
| 88 | + end |
| 89 | + end |
| 90 | + end |
| 91 | + |
| 92 | + ## User callbacks |
| 93 | + |
| 94 | + @doc """ |
| 95 | + A user customizable callback invoked to retrieve default options |
| 96 | + for operations. |
| 97 | + This can be used to provide default values per operation that |
| 98 | + have higher precedence than the values given on configuration. |
| 99 | + """ |
| 100 | + @doc group: "User callbacks" |
| 101 | + @callback default_options(operation) :: Keyword.t() |
| 102 | + when operation: :get | :load | :put | :filter | :reject | :query_one | :query_all |
| 103 | + |
| 104 | + ## Query API |
| 105 | + |
| 106 | + @type record :: any() |
| 107 | + @type predicate :: any() |
| 108 | + @type condition :: any() |
| 109 | + @type queryable :: any() |
| 110 | + @type opts :: Keyword.t() |
| 111 | + |
| 112 | + @doc group: "Query API" |
| 113 | + @callback get([record], [predicate], opts) :: any() |
| 114 | + |
| 115 | + @doc group: "Query API" |
| 116 | + @callback get!([record], [predicate], opts) :: any() |
| 117 | + |
| 118 | + @doc group: "Query API" |
| 119 | + @callback load([record], [predicate], opts) :: any() |
| 120 | + |
| 121 | + @doc group: "Query API" |
| 122 | + @callback load!([record], [predicate], opts) :: any() |
| 123 | + |
| 124 | + @doc group: "Query API" |
| 125 | + @callback put([record], [predicate], opts) :: any() |
| 126 | + |
| 127 | + @doc group: "Query API" |
| 128 | + @callback put!([record], [predicate], opts) :: any() |
| 129 | + |
| 130 | + @doc group: "Query API" |
| 131 | + @callback filter([record], condition, opts) :: any() |
| 132 | + |
| 133 | + @doc group: "Query API" |
| 134 | + @callback reject([record], condition, opts) :: any() |
| 135 | + |
| 136 | + @doc group: "Query API" |
| 137 | + @callback query_all(queryable, condition, opts) :: any() |
| 138 | + |
| 139 | + @doc group: "Query API" |
| 140 | + @callback query_one(queryable, condition, opts) :: any() |
| 141 | +end |
0 commit comments