-
Notifications
You must be signed in to change notification settings - Fork 6
PR to track downstream changes in a fork #12
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
base: master
Are you sure you want to change the base?
Changes from 11 commits
c9d5b4a
21aee94
cd269fa
cf3d6f7
e179f9b
bc17834
2ff19c1
3b70013
bade652
b1abcc1
f849298
3df7d04
6bb34a0
28f5a56
8ee7d94
b9348f2
34f529a
d215432
266442b
e5b6300
e93bc16
eb43295
4683628
c36819b
bd1e1c0
641c2b2
d2b4b85
c926fe0
2c9cf3f
f28fc13
d04bb25
32cba31
eec819c
a8d8d77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| # Used by "mix format" | ||
| [ | ||
| line_length: 110, | ||
| inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"] | ||
| ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -56,10 +56,11 @@ defmodule EctoTrail do | |
|
|
||
| Insert arguments, return and options same as `c:Ecto.Repo.insert/2` has. | ||
| """ | ||
| @spec insert_and_log(struct_or_changeset :: Ecto.Schema.t | Ecto.Changeset.t, | ||
| actor_id :: String.T, | ||
| opts :: Keyword.t) :: | ||
| {:ok, Ecto.Schema.t} | {:error, Ecto.Changeset.t} | ||
| @spec insert_and_log( | ||
| struct_or_changeset :: Ecto.Schema.t() | Ecto.Changeset.t(), | ||
| actor_id :: String.T, | ||
| opts :: Keyword.t() | ||
| ) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()} | ||
| def insert_and_log(struct_or_changeset, actor_id, opts \\ []), | ||
| do: EctoTrail.insert_and_log(__MODULE__, struct_or_changeset, actor_id, opts) | ||
|
|
||
|
|
@@ -68,13 +69,28 @@ defmodule EctoTrail do | |
|
|
||
| Insert arguments, return and options same as `c:Ecto.Repo.update/2` has. | ||
| """ | ||
| @spec update_and_log(changeset :: Ecto.Changeset.t, | ||
| actor_id :: String.T, | ||
| opts :: Keyword.t) :: | ||
| {:ok, Ecto.Schema.t} | | ||
| {:error, Ecto.Changeset.t} | ||
| @spec update_and_log( | ||
| changeset :: Ecto.Changeset.t(), | ||
| actor_id :: String.T, | ||
| opts :: Keyword.t() | ||
| ) :: | ||
| {:ok, Ecto.Schema.t()} | ||
| | {:error, Ecto.Changeset.t()} | ||
| def update_and_log(changeset, actor_id, opts \\ []), | ||
| do: EctoTrail.update_and_log(__MODULE__, changeset, actor_id, opts) | ||
|
|
||
| @doc """ | ||
| Call `c:Ecto.Repo.delete/2` operation and store deleted objext in a `change_log` table. | ||
| """ | ||
| @spec delete_and_log( | ||
| struct_or_changeset :: Ecto.Schema.t() | Ecto.Changeset.t(), | ||
| actor_id :: String.T, | ||
| opts :: Keyword.t() | ||
| ) :: | ||
| {:ok, Ecto.Schema.t()} | ||
| | {:error, Ecto.Changeset.t()} | ||
| def delete_and_log(struct_or_changeset, actor_id, opts \\ []), | ||
| do: EctoTrail.delete_and_log(__MODULE__, struct_or_changeset, actor_id, opts) | ||
| end | ||
| end | ||
|
|
||
|
|
@@ -83,47 +99,68 @@ defmodule EctoTrail do | |
|
|
||
| Insert arguments, return and options same as `c:Ecto.Repo.insert/2` has. | ||
| """ | ||
| @spec insert_and_log(repo :: Ecto.Repo.t, | ||
| struct_or_changeset :: Ecto.Schema.t | Ecto.Changeset.t, | ||
| actor_id :: String.T, | ||
| opts :: Keyword.t) :: | ||
| {:ok, Ecto.Schema.t} | {:error, Ecto.Changeset.t} | ||
| @spec insert_and_log( | ||
| repo :: Ecto.Repo.t(), | ||
| struct_or_changeset :: Ecto.Schema.t() | Ecto.Changeset.t(), | ||
| actor_id :: String.T, | ||
| opts :: Keyword.t() | ||
| ) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()} | ||
| def insert_and_log(repo, struct_or_changeset, actor_id, opts \\ []) do | ||
| Multi.new | ||
| Multi.new() | ||
| |> Multi.insert(:operation, struct_or_changeset, opts) | ||
| |> run_logging_transaction(repo, struct_or_changeset, actor_id) | ||
| |> run_logging_transaction(repo, struct_or_changeset, actor_id, 1) | ||
| end | ||
|
|
||
| @doc """ | ||
| Call `c:Ecto.Repo.update/2` operation and store changes in a `change_log` table. | ||
|
|
||
| Insert arguments, return and options same as `c:Ecto.Repo.update/2` has. | ||
| """ | ||
| @spec update_and_log(repo :: Ecto.Repo.t, | ||
| changeset :: Ecto.Changeset.t, | ||
| actor_id :: String.T, | ||
| opts :: Keyword.t) :: | ||
| {:ok, Ecto.Schema.t} | | ||
| {:error, Ecto.Changeset.t} | ||
| @spec update_and_log( | ||
| repo :: Ecto.Repo.t(), | ||
| changeset :: Ecto.Changeset.t(), | ||
| actor_id :: String.T, | ||
| opts :: Keyword.t() | ||
| ) :: | ||
| {:ok, Ecto.Schema.t()} | ||
| | {:error, Ecto.Changeset.t()} | ||
| def update_and_log(repo, changeset, actor_id, opts \\ []) do | ||
| Multi.new | ||
| Multi.new() | ||
| |> Multi.update(:operation, changeset, opts) | ||
| |> run_logging_transaction(repo, changeset, actor_id) | ||
| |> run_logging_transaction(repo, changeset, actor_id, 2) | ||
| end | ||
|
|
||
| @doc """ | ||
| Call `c:Ecto.Repo.delete/2` operation and store deleted objext in a `change_log` table. | ||
| """ | ||
| @spec delete_and_log( | ||
| repo :: Ecto.Repo.t(), | ||
| struct_or_changeset :: Ecto.Schema.t() | Ecto.Changeset.t(), | ||
| actor_id :: String.T, | ||
| opts :: Keyword.t() | ||
| ) :: | ||
| {:ok, Ecto.Schema.t()} | ||
| | {:error, Ecto.Changeset.t()} | ||
| def delete_and_log(repo, struct_or_changeset, actor_id, opts \\ []) do | ||
| Multi.new() | ||
| |> Multi.delete(:operation, struct_or_changeset, opts) | ||
| |> run_logging_transaction(repo, struct_or_changeset, actor_id, 3) | ||
| end | ||
|
|
||
| defp run_logging_transaction(multi, repo, struct_or_changeset, actor_id) do | ||
| defp run_logging_transaction(multi, repo, struct_or_changeset, actor_id, operation_type) do | ||
| multi | ||
| |> Multi.run(:changelog, &log_changes(repo, &1, struct_or_changeset, actor_id)) | ||
| |> Multi.run(:changelog, &log_changes(repo, &1, struct_or_changeset, actor_id, operation_type)) | ||
| |> repo.transaction() | ||
| |> build_result() | ||
| end | ||
|
|
||
| defp build_result({:ok, %{operation: operation}}), | ||
| do: {:ok, operation} | ||
|
|
||
| defp build_result({:error, :operation, reason, _changes_so_far}), | ||
| do: {:error, reason} | ||
|
|
||
| defp log_changes(repo, multi_acc, struct_or_changeset, actor_id) do | ||
| defp log_changes(repo, multi_acc, struct_or_changeset, actor_id, operation_type) do | ||
| %{operation: operation} = multi_acc | ||
| associations = operation.__struct__.__schema__(:associations) | ||
| resource = operation.__struct__.__schema__(:source) | ||
|
|
@@ -134,6 +171,7 @@ defmodule EctoTrail do | |
| |> get_changes() | ||
| |> get_embed_changes(embeds) | ||
| |> get_assoc_changes(associations) | ||
| |> validate_changes(struct_or_changeset, operation_type) | ||
|
|
||
| result = | ||
| %{ | ||
|
|
@@ -148,25 +186,68 @@ defmodule EctoTrail do | |
| case result do | ||
| {:ok, changelog} -> | ||
| {:ok, changelog} | ||
|
|
||
| {:error, reason} -> | ||
| Logger.error("Failed to store changes in audit log: #{inspect struct_or_changeset} " <> | ||
| "by actor #{inspect actor_id}. Reason: #{inspect reason}") | ||
| Logger.error( | ||
| "Failed to store changes in audit log: #{inspect(struct_or_changeset)} " <> | ||
| "by actor #{inspect(actor_id)}. Reason: #{inspect(reason)}" | ||
| ) | ||
|
|
||
| {:ok, reason} | ||
| end | ||
| end | ||
|
|
||
| defp validate_changes(changes, schema, operation_type) do | ||
| case operation_type do | ||
| 1 -> | ||
| # this case is entered when the operation type it's an insert operation | ||
| changes | ||
|
|
||
| 2 -> | ||
| # this case is entered when the operation type it's an update operation | ||
| changes | ||
|
|
||
| 3 -> | ||
| # this case is entered when the operation type it's a delete operation | ||
| {_, regreso} = | ||
| Map.from_struct(schema) | ||
| |> Map.pop(:__meta__) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pipe chain should start with a raw value. |
||
|
|
||
| IO.inspect(regreso) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There should be no calls to IO.inspect/1. |
||
| remove_empty_assosiations(regreso) | ||
| end | ||
| end | ||
|
|
||
| defp remove_empty_assosiations(struct) do | ||
| Enum.map(struct, fn {key, value} -> | ||
| {key, | ||
| if String.contains?(Kernel.inspect(value), "Ecto.Association.NotLoaded") do | ||
| nil | ||
| else | ||
| value | ||
| end} | ||
| end) | ||
| |> Map.new() | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pipe chain should start with a raw value. |
||
| end | ||
|
|
||
| defp get_changes(%Changeset{changes: changes}), | ||
| do: map_custom_ecto_types(changes) | ||
|
|
||
| defp get_changes(changes) when is_map(changes), | ||
| do: changes |> Changeset.change(%{}) |> get_changes() | ||
|
|
||
| defp get_changes(changes) when is_list(changes), | ||
| do: changes |> Enum.map_reduce([], fn(ch, acc) -> {nil, List.insert_at(acc, -1, get_changes(ch))} end) |> elem(1) | ||
| do: | ||
| changes | ||
| |> Enum.map_reduce([], fn ch, acc -> {nil, List.insert_at(acc, -1, get_changes(ch))} end) | ||
| |> elem(1) | ||
|
|
||
| defp get_embed_changes(changeset, embeds) do | ||
| Enum.reduce(embeds, changeset, fn embed, changeset -> | ||
| case Map.get(changeset, embed) do | ||
| nil -> | ||
| changeset | ||
|
|
||
| embed_changes -> | ||
| Map.put(changeset, embed, get_changes(embed_changes)) | ||
| end | ||
|
|
@@ -178,6 +259,7 @@ defmodule EctoTrail do | |
| case Map.get(changeset, assoc) do | ||
| nil -> | ||
| changeset | ||
|
|
||
| assoc_changes -> | ||
| Map.put(changeset, assoc, get_changes(assoc_changes)) | ||
| end | ||
|
|
@@ -204,7 +286,7 @@ defmodule EctoTrail do | |
| :actor_id, | ||
| :resource, | ||
| :resource_id, | ||
| :changeset, | ||
| :changeset | ||
| ]) | ||
| end | ||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be a final \n at the end of each file.