Skip to content

Commit

Permalink
feat: add delegate datalayer + support data layer context
Browse files Browse the repository at this point in the history
  • Loading branch information
zachdaniel committed Aug 21, 2020
1 parent 223e88f commit b2ca828
Show file tree
Hide file tree
Showing 19 changed files with 470 additions and 126 deletions.
3 changes: 3 additions & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ locals_without_parens = [
allow_nil?: 1,
attribute: 2,
attribute: 3,
authorize?: 1,
base_filter: 1,
belongs_to: 2,
belongs_to: 3,
constraints: 1,
Expand Down Expand Up @@ -45,6 +47,7 @@ locals_without_parens = [
source_field_on_join_table: 1,
table: 1,
through: 1,
to: 1,
type: 1,
update: 1,
update: 2,
Expand Down
27 changes: 13 additions & 14 deletions lib/ash/actions/destroy.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ defmodule Ash.Actions.Destroy do
alias Ash.Engine
alias Ash.Engine.Request

@spec run(Ash.api(), Ash.record(), Ash.action(), Keyword.t()) ::
@spec run(Ash.api(), Ash.Changeset.t(), Ash.action(), Keyword.t()) ::
:ok | {:error, Ash.Changeset.t()} | {:error, Ash.error()}
def run(api, %resource{} = record, action, opts) do
def run(api, %{data: record, resource: resource} = changeset, action, opts) do
engine_opts =
opts
|> Keyword.take([:verbose?, :actor, :authorize?])
Expand All @@ -28,20 +28,19 @@ defmodule Ash.Actions.Destroy do
path: [:destroy],
action: action,
authorize?: false,
changeset: %{changeset | action_type: :destroy, api: api},
data:
Request.resolve([[:data, :data]], fn _ ->
changeset =
record
|> Ash.Changeset.new()
|> Map.put(:api, api)

with :ok <- validate(changeset),
:ok <- Ash.Resource.data_layer(resource).destroy(record) do
{:ok, record}
else
{:error, error} -> {:error, error}
Request.resolve(
[[:data, :data], [:destroy, :changeset]],
fn %{destroy: %{changeset: changeset}} ->
with :ok <- validate(changeset),
:ok <- Ash.DataLayer.destroy(resource, changeset) do
{:ok, record}
else
{:error, error} -> {:error, error}
end
end
end)
)
)

case Engine.run([authorization_request, destroy_request], api, engine_opts) do
Expand Down
4 changes: 2 additions & 2 deletions lib/ash/actions/relationships.ex
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ defmodule Ash.Actions.Relationships do
end

defp destroy_and_remove(api, join_row, to_remove_record, record, relationship, join_pkey, pkey) do
case api.destroy(join_row) do
case api.destroy(Ash.Changeset.new(join_row)) do
:ok ->
{:ok,
record
Expand Down Expand Up @@ -779,7 +779,7 @@ defmodule Ash.Actions.Relationships do
|> Enum.reduce(record, fn relationship, record ->
not_loaded = %Ecto.Association.NotLoaded{
__field__: relationship.name,
__owner__: relationship.source,
__owner__: resource,
__cardinality__: relationship.cardinality
}

Expand Down
29 changes: 17 additions & 12 deletions lib/ash/api/api.ex
Original file line number Diff line number Diff line change
Expand Up @@ -159,46 +159,46 @@ defmodule Ash.Api do
#{NimbleOptions.docs(@create_opts_schema)}
"""
@callback create!(resource :: Ash.resource(), params :: Keyword.t()) ::
@callback create!(Ash.changeset(), params :: Keyword.t()) ::
Ash.record() | no_return

@doc """
Create a record.
#{NimbleOptions.docs(@create_opts_schema)}
"""
@callback create(resource :: Ash.resource(), params :: Keyword.t()) ::
@callback create(Ash.changeset(), params :: Keyword.t()) ::
{:ok, Ash.record()} | {:error, Ash.error()}

@doc """
Update a record. See `c:update/2` for more information.
#{NimbleOptions.docs(@update_opts_schema)}
"""
@callback update!(record :: Ash.record(), params :: Keyword.t()) ::
@callback update!(Ash.changeset(), params :: Keyword.t()) ::
Ash.record() | no_return

@doc """
Update a record.
#{NimbleOptions.docs(@update_opts_schema)}
"""
@callback update(record :: Ash.record(), params :: Keyword.t()) ::
@callback update(Ash.changeset(), params :: Keyword.t()) ::
{:ok, Ash.record()} | {:error, Ash.error()}

@doc """
Destroy a record. See `c:destroy/2` for more information.
#{NimbleOptions.docs(@destroy_opts_schema)}
"""
@callback destroy!(record :: Ash.record(), params :: Keyword.t()) :: :ok | no_return
@callback destroy!(Ash.changeset() | Ash.record(), params :: Keyword.t()) :: :ok | no_return

@doc """
Destroy a record.
#{NimbleOptions.docs(@destroy_opts_schema)}
"""
@callback destroy(record :: Ash.record(), params :: Keyword.t()) ::
@callback destroy(Ash.changeset() | Ash.record(), params :: Keyword.t()) ::
:ok | {:error, Ash.error()}

@doc """
Expand Down Expand Up @@ -458,25 +458,30 @@ defmodule Ash.Api do
end

@doc false
@spec destroy!(Ash.api(), Ash.record(), Keyword.t()) :: :ok | no_return
def destroy!(api, record, opts) do
@spec destroy!(Ash.api(), Ash.changeset() | Ash.record(), Keyword.t()) :: :ok | no_return
def destroy!(api, changeset, opts) do
opts = NimbleOptions.validate!(opts, @destroy_opts_schema)

api
|> destroy(record, opts)
|> destroy(changeset, opts)
|> unwrap_or_raise!()
end

@doc false
@spec destroy(Ash.api(), Ash.record(), Keyword.t()) :: :ok | {:error, Ash.error()}
def destroy(api, %resource{} = record, opts) do
@spec destroy(Ash.api(), Ash.changeset() | Ash.record(), Keyword.t()) ::
:ok | {:error, Ash.error()}
def destroy(api, %Ash.Changeset{resource: resource} = changeset, opts) do
with {:ok, opts} <- NimbleOptions.validate(opts, @destroy_opts_schema),
{:ok, resource} <- Ash.Api.resource(api, resource),
{:ok, action} <- get_action(resource, opts, :destroy) do
Destroy.run(api, record, action, opts)
Destroy.run(api, changeset, action, opts)
end
end

def destroy(api, record, opts) do
destroy(api, Ash.Changeset.new(record), opts)
end

defp get_action(resource, params, type) do
case Keyword.fetch(params, :action) do
{:ok, %_{} = action} ->
Expand Down
16 changes: 1 addition & 15 deletions lib/ash/api/interface.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,26 @@ defmodule Ash.Api.Interface do
@moduledoc false

defmacro __using__(_) do
quote location: :keep do
quote do
alias Ash.Api

@impl true
def get!(resource, id, params \\ []) do
Api.get!(__MODULE__, resource, id, params)
end

@impl true
def get(resource, id, params \\ []) do
case Api.get(__MODULE__, resource, id, params) do
{:ok, instance} -> {:ok, instance}
{:error, error} -> {:error, List.wrap(error)}
end
end

@impl true
def read!(query, opts \\ [])

def read!(query, opts) do
Api.read!(__MODULE__, query, opts)
end

@impl true
def read(query, opts \\ [])

def read(query, opts) do
Expand All @@ -35,65 +31,55 @@ defmodule Ash.Api.Interface do
end
end

@impl true
def load!(data, query, opts \\ []) do
Api.load!(__MODULE__, data, query, opts)
end

@impl true
def load(data, query, opts \\ []) do
case Api.load(__MODULE__, data, query, opts) do
{:ok, results} -> {:ok, results}
{:error, error} -> {:error, List.wrap(error)}
end
end

@impl true
def create!(changeset, params \\ []) do
Api.create!(__MODULE__, changeset, params)
end

@impl true
def create(changeset, params \\ []) do
case Api.create(__MODULE__, changeset, params) do
{:ok, instance} -> {:ok, instance}
{:error, error} -> {:error, List.wrap(error)}
end
end

@impl true
def update!(changeset, params \\ []) do
Api.update!(__MODULE__, changeset, params)
end

@impl true
def update(changeset, params \\ []) do
case Api.update(__MODULE__, changeset, params) do
{:ok, instance} -> {:ok, instance}
{:error, error} -> {:error, List.wrap(error)}
end
end

@impl true
def destroy!(record, params \\ []) do
Api.destroy!(__MODULE__, record, params)
end

@impl true
def destroy(record, params \\ []) do
case Api.destroy(__MODULE__, record, params) do
:ok -> :ok
{:error, error} -> {:error, List.wrap(error)}
end
end

@impl true
def reload!(%resource{} = record, params \\ []) do
id = record |> Map.take(Ash.Resource.primary_key(resource)) |> Enum.to_list()
get!(resource, id, params)
end

@impl true
def reload(%resource{} = record, params \\ []) do
id = record |> Map.take(Ash.Resource.primary_key(resource)) |> Enum.to_list()
get(resource, id, params)
Expand Down
5 changes: 4 additions & 1 deletion lib/ash/api/transformers/ensure_resources_compiled.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ defmodule Ash.Api.Transformers.EnsureResourcesCompiled do
use Ash.Dsl.Transformer

alias Ash.Dsl.Transformer
require Logger

def transform(_module, dsl) do
dsl
Expand All @@ -25,7 +26,9 @@ defmodule Ash.Api.Transformers.EnsureResourcesCompiled do
[] ->
{:ok, dsl}

_ ->
resources ->
Logger.error("Could not ensure that resources #{inspect(resources)} were compiled")

:halt
end
end
Expand Down
19 changes: 15 additions & 4 deletions lib/ash/changeset/changeset.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ defmodule Ash.Changeset do
:action_type,
:resource,
:api,
data_layer_context: %{},
after_action: [],
before_action: [],
errors: [],
Expand Down Expand Up @@ -169,6 +170,16 @@ defmodule Ash.Changeset do
Map.get(changeset.data, attribute)
end

@spec put_datalayer_context(t(), atom, term) :: t()
def put_datalayer_context(changeset, key, value) do
%{changeset | data_layer_context: Map.put(changeset.data_layer_context, key, value)}
end

@spec set_datalayer_context(t(), map) :: t()
def set_datalayer_context(changeset, map) do
%{changeset | data_layer_context: Map.merge(changeset.data_layer_context, map)}
end

@doc """
Appends a record of list of records to a relationship. Stacks with previous removals/additions.
Expand Down Expand Up @@ -209,7 +220,7 @@ defmodule Ash.Changeset do
message: "Relationship is not editable"
)

{:error, error}
add_error(changeset, error)

%{type: :many_to_many} = relationship ->
case primary_keys_with_changes(relationship, List.wrap(record_or_records)) do
Expand Down Expand Up @@ -278,7 +289,7 @@ defmodule Ash.Changeset do
message: "Relationship is not editable"
)

{:error, error}
add_error(changeset, error)

relationship ->
case primary_key(relationship, List.wrap(record_or_records)) do
Expand Down Expand Up @@ -324,7 +335,7 @@ defmodule Ash.Changeset do
@spec replace_relationship(
t(),
atom(),
Ash.primary_key() | [Ash.primary_key()]
Ash.primary_key() | [Ash.primary_key()] | nil
) :: t()
def replace_relationship(changeset, relationship, record_or_records) do
case Ash.Resource.relationship(changeset.resource, relationship) do
Expand All @@ -344,7 +355,7 @@ defmodule Ash.Changeset do
message: "Relationship is not editable"
)

{:error, error}
add_error(changeset, error)

%{cardinality: :one, type: type}
when is_list(record_or_records) and length(record_or_records) > 1 ->
Expand Down

0 comments on commit b2ca828

Please sign in to comment.