Skip to content

Commit

Permalink
Support Ecto Embed
Browse files Browse the repository at this point in the history
  • Loading branch information
maennchen committed Dec 15, 2020
1 parent 1aaf462 commit 98443db
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 12 deletions.
49 changes: 46 additions & 3 deletions lib/paper_trail/serializer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ defmodule PaperTrail.Serializer do
Dumps changes using Ecto fields
"""
@spec serialize_changes(Ecto.Changeset.t()) :: map()
def serialize_changes(%Ecto.Changeset{data: %schema{}, changes: changes}) do
changes
|> schema.__struct__()
def serialize_changes(%Ecto.Changeset{changes: changes} = changeset) do
changeset
|> serialize_model_changes()
|> serialize()
|> Map.take(Map.keys(changes))
end
Expand Down Expand Up @@ -147,4 +147,47 @@ defmodule PaperTrail.Serializer do
"#{model_id}"
end
end

@spec serialize_model_changes(Ecto.Changeset.t()) :: map()
defp serialize_model_changes(%Ecto.Changeset{data: %schema{}} = changeset) do
field_values = serialize_model_field_changes(changeset)
embed_values = serialize_model_embed_changes(changeset)

field_values
|> Map.merge(embed_values)
|> schema.__struct__()
end

defp serialize_model_field_changes(%Ecto.Changeset{data: %schema{}, changes: changes}) do
change_keys = changes |> Map.keys() |> MapSet.new()

field_keys =
:fields
|> schema.__schema__()
|> MapSet.new()
|> MapSet.intersection(change_keys)
|> MapSet.to_list()

Map.take(changes, field_keys)
end

defp serialize_model_embed_changes(%Ecto.Changeset{data: %schema{}, changes: changes}) do
change_keys = changes |> Map.keys() |> MapSet.new()

embed_keys =
:embeds
|> schema.__schema__()
|> MapSet.new()
|> MapSet.intersection(change_keys)
|> MapSet.to_list()

changes
|> Map.take(embed_keys)
|> Map.new(fn {key, value} ->
case schema.__schema__(:embed, key) do
%Ecto.Embedded{cardinality: :one} -> {key, serialize_model_changes(value)}
%Ecto.Embedded{cardinality: :many} -> {key, Enum.map(value, &serialize_model_changes/1)}
end
end)
end
end
2 changes: 2 additions & 0 deletions priv/repo/migrations/20160619190938_add_simple_people.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ defmodule Repo.Migrations.CreateSimplePeople do
add :visit_count, :integer
add :gender, :boolean
add :birthdate, :date
add :singular, :map
add :plural, {:array, :map}

add :company_id, references(:simple_companies), null: false

Expand Down
129 changes: 123 additions & 6 deletions test/paper_trail/bang_functions_simple_mode_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,9 @@ defmodule PaperTrailTest.SimpleModeBangFunctions do
gender: true,
visit_count: nil,
birthdate: nil,
company_id: second_company.id
company_id: second_company.id,
plural: [],
singular: nil
}

assert Map.drop(version, [:id, :inserted_at]) == %{
Expand All @@ -342,6 +344,39 @@ defmodule PaperTrailTest.SimpleModeBangFunctions do
assert person == first(Person, :id) |> repo().one |> serialize
end

test "creating a person with embeds creates a person version with correct attributes" do
company = create_company_with_version()

%Person{
id: person_id,
plural: [%{id: _, name: "Plural"}],
singular: %{id: _, name: "Singular"}
} =
person =
%Person{}
|> Person.changeset(%{
first_name: "Izel",
last_name: "Nakri",
gender: true,
company_id: company.id,
plural: [%{name: "Plural"}],
singular: %{name: "Singular"}
})
|> PaperTrail.insert!()

version = PaperTrail.get_version(person)
person_change = person |> serialize() |> convert_to_string_map

assert %{
event: "insert",
item_type: "SimplePerson",
item_id: ^person_id,
item_changes: ^person_change
} = version

assert person == first(Person, :id) |> repo().one
end

test "updating a person creates a person version with correct attributes" do
inserted_initial_company =
create_company_with_version(%{
Expand Down Expand Up @@ -391,7 +426,9 @@ defmodule PaperTrailTest.SimpleModeBangFunctions do
visit_count: 10,
birthdate: ~D[1992-04-01],
last_name: "Nakri",
gender: true
gender: true,
plural: [],
singular: nil
}

assert Map.drop(version, [:id, :inserted_at]) == %{
Expand All @@ -413,6 +450,44 @@ defmodule PaperTrailTest.SimpleModeBangFunctions do
assert person == first(Person, :id) |> repo().one |> serialize
end

test "updating a person with embeds creates a person version with correct attributes" do
company = create_company_with_version()

%Person{
id: person_id,
plural: [%{id: _, name: "Plural"}],
singular: %{id: _, name: "Singular"}
} =
person =
%Person{}
|> Person.changeset(%{
first_name: "Izel",
last_name: "Nakri",
gender: true,
company_id: company.id
})
|> PaperTrail.insert!()
|> Person.changeset(%{
plural: [%{name: "Plural"}],
singular: %{name: "Singular"}
})
|> PaperTrail.update!()

version = PaperTrail.get_version(person)

assert %{
event: "update",
item_type: "SimplePerson",
item_id: ^person_id,
item_changes: %{
"plural" => [%{"id" => _, "name" => "Plural"}],
"singular" => %{"id" => _, "name" => "Singular"}
}
} = version

assert person == first(Person, :id) |> repo().one
end

test "deleting a person creates a person version with correct attributes" do
create_company_with_version(%{name: "Acme LLC", website: "http://www.acme.com"})

Expand Down Expand Up @@ -475,7 +550,9 @@ defmodule PaperTrailTest.SimpleModeBangFunctions do
gender: true,
visit_count: 10,
birthdate: ~D[1992-04-01],
company_id: inserted_target_company.id
company_id: inserted_target_company.id,
plural: [],
singular: nil
}),
originator_id: nil,
origin: "admin",
Expand All @@ -485,6 +562,40 @@ defmodule PaperTrailTest.SimpleModeBangFunctions do
assert old_person == person_before_deletion
end

test "deleting a person with embeds creates a person version with correct attributes" do
company = create_company_with_version()

%Person{
id: person_id,
plural: [%{id: _, name: "Plural"}],
singular: %{id: _, name: "Singular"}
} =
person =
%Person{}
|> Person.changeset(%{
first_name: "Izel",
last_name: "Nakri",
gender: true,
company_id: company.id,
plural: [%{name: "Plural"}],
singular: %{name: "Singular"}
})
|> PaperTrail.insert!()
|> PaperTrail.delete!()

version = PaperTrail.get_version(person)
person_change = person |> serialize() |> convert_to_string_map

assert %{
event: "delete",
item_type: "SimplePerson",
item_id: ^person_id,
item_changes: ^person_change
} = version

assert is_nil(first(Person, :id) |> repo().one)
end

# Multi tenant tests
test "[multi tenant] creating a company creates a company version with correct attributes" do
tenant = MultiTenant.tenant()
Expand Down Expand Up @@ -791,7 +902,9 @@ defmodule PaperTrailTest.SimpleModeBangFunctions do
gender: true,
visit_count: nil,
birthdate: nil,
company_id: second_company.id
company_id: second_company.id,
plural: [],
singular: nil
}

assert Map.drop(version, [:id, :inserted_at]) == %{
Expand Down Expand Up @@ -863,7 +976,9 @@ defmodule PaperTrailTest.SimpleModeBangFunctions do
visit_count: 10,
birthdate: ~D[1992-04-01],
last_name: "Nakri",
gender: true
gender: true,
plural: [],
singular: nil
}

assert Map.drop(version, [:id, :inserted_at]) == %{
Expand Down Expand Up @@ -955,7 +1070,9 @@ defmodule PaperTrailTest.SimpleModeBangFunctions do
gender: true,
visit_count: 10,
birthdate: ~D[1992-04-01],
company_id: inserted_target_company.id
company_id: inserted_target_company.id,
plural: [],
singular: nil
}),
originator_id: nil,
origin: "admin",
Expand Down
12 changes: 9 additions & 3 deletions test/paper_trail/base_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,9 @@ defmodule PaperTrailTest do
gender: true,
visit_count: nil,
birthdate: nil,
company_id: new_company_result[:model].id
company_id: new_company_result[:model].id,
plural: [],
singular: nil
}

assert Map.drop(version, [:id, :inserted_at]) == %{
Expand Down Expand Up @@ -475,7 +477,9 @@ defmodule PaperTrailTest do
visit_count: 10,
birthdate: ~D[1992-04-01],
last_name: "Nakri",
gender: true
gender: true,
plural: [],
singular: nil
}

assert Map.drop(version, [:id, :inserted_at]) == %{
Expand Down Expand Up @@ -557,7 +561,9 @@ defmodule PaperTrailTest do
gender: true,
visit_count: 10,
birthdate: ~D[1992-04-01],
company_id: target_company_insertion[:model].id
company_id: target_company_insertion[:model].id,
plural: [],
singular: nil
},
originator_id: nil,
origin: "admin",
Expand Down
20 changes: 20 additions & 0 deletions test/support/simple_models.exs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ defmodule SimplePerson do

belongs_to(:company, SimpleCompany, foreign_key: :company_id)

embeds_one(:singular, SimpleEmbed)
embeds_many(:plural, SimpleEmbed)

timestamps()
end

Expand All @@ -113,6 +116,8 @@ defmodule SimplePerson do
model
|> cast(params, @optional_fields)
|> foreign_key_constraint(:company_id)
|> cast_embed(:singular)
|> cast_embed(:plural)
end

def count do
Expand All @@ -125,3 +130,18 @@ defmodule SimplePerson do
|> PaperTrail.RepoClient.repo().one
end
end

defmodule SimpleEmbed do
use Ecto.Schema

import Ecto.Changeset

embedded_schema do
field(:name, :string)
end

def changeset(model, params \\ %{}) do
model
|> cast(params, [:name])
end
end

0 comments on commit 98443db

Please sign in to comment.