Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c9d5b4a
Set PR template
valiotbot Nov 21, 2018
21aee94
remove uuid change to normal id
gdavila09 Nov 28, 2018
cd269fa
mix format
acrogenesis Dec 5, 2018
cf3d6f7
working tests
acrogenesis Dec 5, 2018
e179f9b
Merge pull request #1 from valiot/gdavila/TEAPP-30-uuid_to_id
acrogenesis Dec 5, 2018
bc17834
update deps
gdavila09 Dec 5, 2018
2ff19c1
removed updated at false
gdavila09 Dec 5, 2018
3b70013
Merge pull request #2 from valiot/gdavila/update-ecto
acrogenesis Dec 6, 2018
bade652
delete and log function
gdavila09 Dec 17, 2018
b1abcc1
Merge pull request #3 from valiot/gdavila/TEAPP-23-delete-and-log
acrogenesis Dec 19, 2018
f849298
remove unused variable
acrogenesis Jan 2, 2019
3df7d04
upsert in ecto trail
gdavila09 Jan 8, 2019
6bb34a0
remove dogma, update deps
acrogenesis Jan 16, 2019
28f5a56
fix Multi.run deprecation warning
acrogenesis Jan 16, 2019
8ee7d94
Merge pull request #4 from valiot/gdavila/TEAPP-70-upsert-log
acrogenesis Jan 16, 2019
b9348f2
passing tests
acrogenesis Jan 29, 2019
34f529a
log the type of change
gdavila09 Jan 24, 2019
d215432
changed type from created to insert
gdavila09 Jan 29, 2019
266442b
change enum to insert and test upsert
gdavila09 Jan 30, 2019
e5b6300
fix test
gdavila09 Jan 30, 2019
e93bc16
Merge pull request #5 from valiot/gdavila/TEAPP-84-log-type-change
acrogenesis Jan 30, 2019
eb43295
update de readme
gdavila09 Feb 20, 2019
4683628
Merge pull request #6 from valiot/gdavila/TEAPP-84-log-type-change
acrogenesis Feb 20, 2019
c36819b
rename ChangeEnum
acrogenesis Feb 21, 2019
bd1e1c0
update deps
acrogenesis Sep 30, 2021
641c2b2
update dialyxir
acrogenesis Sep 30, 2021
d2b4b85
redact custom fields from changeset
acrogenesis Sep 30, 2021
c926fe0
solve breaking change, redacted_fields not required
acrogenesis Sep 30, 2021
2c9cf3f
Merge pull request #8 from valiot/maintenance
gdavila09 Oct 2, 2021
f28fc13
change data structure for delete changesets
afloram Feb 21, 2022
d04bb25
update dependencies
afloram Feb 22, 2022
32cba31
Merge pull request #9 from valiot/alex/VAPP-136-format-for-delete-cha…
acrogenesis Mar 1, 2022
eec819c
Corral/log only (#10)
doctorcorral Nov 14, 2023
a8d8d77
remove unused vars
acrogenesis Nov 22, 2023
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
5 changes: 5 additions & 0 deletions .formatter.exs
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}"]
]
Copy link
Copy Markdown
Member Author

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.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ GitHub.sublime-settings
.tags_sorted_by_file
.vagrant
.DS_Store
.elixir_ls

# Ignore released binaries
.deliver
Expand Down
2 changes: 1 addition & 1 deletion config/.credo.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
checks: [
{Credo.Check.Design.TagTODO, exit_status: 0},
{Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 120},
{Credo.Check.Readability.Specs, exit_status: 0},
{Credo.Check.Readability.Specs, exit_status: 0}
]
}
]
Expand Down
8 changes: 4 additions & 4 deletions config/dogma.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ alias Dogma.Rule
config :dogma,
rule_set: Dogma.RuleSet.All,
exclude: [
~r(\Adeps/),
~r(\Adeps/)
],
override: [
%Rule.LineLength{ max_length: 120 },
%Rule.InfixOperatorPadding{ enabled: false },
%Rule.FunctionArity{ max: 5 },
%Rule.LineLength{max_length: 120},
%Rule.InfixOperatorPadding{enabled: false},
%Rule.FunctionArity{max: 5}
]
11 changes: 5 additions & 6 deletions lib/ecto_trail/changelog.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ defmodule EctoTrail.Changelog do
"""
use Ecto.Schema

@primary_key {:id, :binary_id, autogenerate: true}
schema Application.fetch_env!(:ecto_trail, :table_name) do
field :actor_id, :string
field :resource, :string
field :resource_id, :string
field :changeset, :map
field(:actor_id, :string)
field(:resource, :string)
field(:resource_id, :string)
field(:changeset, :map)

timestamps([type: :utc_datetime, updated_at: false])
timestamps(type: :utc_datetime, updated_at: false)
end
end
144 changes: 113 additions & 31 deletions lib/ecto_trail/ecto_trail.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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

Expand All @@ -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)
Expand All @@ -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 =
%{
Expand All @@ -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__)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pipe chain should start with a raw value.


IO.inspect(regreso)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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()
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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
Expand All @@ -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
Expand All @@ -204,7 +286,7 @@ defmodule EctoTrail do
:actor_id,
:resource,
:resource_id,
:changeset,
:changeset
])
end
end
60 changes: 33 additions & 27 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@ defmodule EctoTrail.Mixfile do
@version "0.2.4"

def project do
[app: :ecto_trail,
description: description(),
package: package(),
version: @version,
elixir: "~> 1.4",
elixirc_paths: elixirc_paths(Mix.env),
compilers: [] ++ Mix.compilers,
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps(),
test_coverage: [tool: ExCoveralls],
preferred_cli_env: [coveralls: :test],
docs: [source_ref: "v#\{@version\}", main: "readme", extras: ["README.md"]]]
[
app: :ecto_trail,
description: description(),
package: package(),
version: @version,
elixir: "~> 1.4",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [] ++ Mix.compilers(),
build_embedded: Mix.env() == :prod,
start_permanent: Mix.env() == :prod,
deps: deps(),
test_coverage: [tool: ExCoveralls],
preferred_cli_env: [coveralls: :test],
docs: [source_ref: "v#\{@version\}", main: "readme", extras: ["README.md"]]
]
end

def description do
Expand All @@ -28,24 +30,28 @@ defmodule EctoTrail.Mixfile do
end

defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]
defp elixirc_paths(_), do: ["lib"]

defp deps do
[{:ecto, "~> 2.1"},
{:postgrex, "~> 0.13.2", optional: true},
{:dialyxir, "~> 0.5", only: [:dev, :test], runtime: false},
{:geo, "~> 1.4", only: [:dev, :test]},
{:ex_doc, ">= 0.15.0", only: [:dev, :test]},
{:excoveralls, ">= 0.5.0", only: [:dev, :test]},
{:dogma, ">= 0.1.12", only: [:dev, :test]},
{:credo, ">= 0.5.1", only: [:dev, :test]}]
[
{:ecto_sql, "~> 3.0"},
{:postgrex, ">= 0.14.0"},
{:dialyxir, "~> 0.5", only: [:dev, :test], runtime: false},
{:geo, "~> 3.0.0", only: [:dev, :test]},
{:ex_doc, ">= 0.15.0", only: [:dev, :test]},
{:excoveralls, ">= 0.5.0", only: [:dev, :test]},
{:dogma, ">= 0.1.12", only: [:dev, :test]},
{:credo, ">= 0.5.1", only: [:dev, :test]}
]
end

defp package do
[contributors: ["Nebo #15"],
maintainers: ["Nebo #15"],
licenses: ["LISENSE.md"],
links: %{github: "https://github.com/Nebo15/ecto_trail"},
files: ~w(lib LICENSE.md mix.exs README.md)]
[
contributors: ["Nebo #15"],
maintainers: ["Nebo #15"],
licenses: ["LISENSE.md"],
links: %{github: "https://github.com/Nebo15/ecto_trail"},
files: ~w(lib LICENSE.md mix.exs README.md)
]
end
end
Loading