Add an unique index on posts URLs
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
8caf1e302b
commit
7bb8568504
|
@ -96,6 +96,7 @@ defmodule Mobilizon.Posts.Post do
|
||||||
|> TitleSlug.unique_constraint()
|
|> TitleSlug.unique_constraint()
|
||||||
|> maybe_generate_url()
|
|> maybe_generate_url()
|
||||||
|> validate_required(@required_attrs -- [:slug, :url])
|
|> validate_required(@required_attrs -- [:slug, :url])
|
||||||
|
|> unique_constraint(:url)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_generate_id(%Ecto.Changeset{} = changeset) do
|
defp maybe_generate_id(%Ecto.Changeset{} = changeset) do
|
||||||
|
|
73
priv/repo/migrations/20210622133516_cleanup_posts.exs
Normal file
73
priv/repo/migrations/20210622133516_cleanup_posts.exs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
defmodule Mobilizon.Storage.Repo.Migrations.CleanupPosts do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def up do
|
||||||
|
# Make sure we don't have any duplicate posts
|
||||||
|
rows = fetch_bad_rows()
|
||||||
|
Enum.each(rows, &process_row/1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
# No way down
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fetch_bad_rows() do
|
||||||
|
%Postgrex.Result{rows: rows} =
|
||||||
|
Ecto.Adapters.SQL.query!(
|
||||||
|
Mobilizon.Storage.Repo,
|
||||||
|
"SELECT * FROM (
|
||||||
|
SELECT id, url,
|
||||||
|
ROW_NUMBER() OVER(PARTITION BY url ORDER BY id asc) AS Row
|
||||||
|
FROM posts
|
||||||
|
) dups
|
||||||
|
WHERE dups.Row > 1;"
|
||||||
|
)
|
||||||
|
|
||||||
|
rows
|
||||||
|
end
|
||||||
|
|
||||||
|
defp process_row([id, url, _row]) do
|
||||||
|
first_id = find_first_post_id(url)
|
||||||
|
|
||||||
|
if id != first_id do
|
||||||
|
repair_post_medias(id, first_id)
|
||||||
|
repair_post_tags(id, first_id)
|
||||||
|
delete_row(id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp find_first_post_id(url) do
|
||||||
|
%Postgrex.Result{rows: [[id]]} =
|
||||||
|
Ecto.Adapters.SQL.query!(
|
||||||
|
Mobilizon.Storage.Repo,
|
||||||
|
"SELECT id FROM posts WHERE url = $1 order by inserted_at asc limit 1",
|
||||||
|
[url]
|
||||||
|
)
|
||||||
|
|
||||||
|
id
|
||||||
|
end
|
||||||
|
|
||||||
|
defp repair_post_medias(id, first_id) do
|
||||||
|
Ecto.Adapters.SQL.query!(
|
||||||
|
Mobilizon.Storage.Repo,
|
||||||
|
"UPDATE post_medias SET post_id = $1 WHERE post_id = $2",
|
||||||
|
[first_id, id]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp repair_post_tags(id, first_id) do
|
||||||
|
Ecto.Adapters.SQL.query!(
|
||||||
|
Mobilizon.Storage.Repo,
|
||||||
|
"UPDATE post_tags SET post_id = $1 WHERE post_id = $2",
|
||||||
|
[first_id, id]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp delete_row(id) do
|
||||||
|
Ecto.Adapters.SQL.query!(
|
||||||
|
Mobilizon.Storage.Repo,
|
||||||
|
"DELETE FROM posts WHERE id = $1",
|
||||||
|
[id]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
11
priv/repo/migrations/20210622133555_add_index_to_posts.exs
Normal file
11
priv/repo/migrations/20210622133555_add_index_to_posts.exs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
defmodule Mobilizon.Storage.Repo.Migrations.AddIndexToPosts do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def up do
|
||||||
|
create_if_not_exists(unique_index("posts", [:url]))
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
drop_if_exists(index("posts", [:url]))
|
||||||
|
end
|
||||||
|
end
|
|
@ -42,6 +42,29 @@ defmodule Mobilizon.PostsTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "create_post/1 with an already existing URL returns error changeset" do
|
||||||
|
%Actor{} = actor = insert(:actor)
|
||||||
|
%Actor{} = group = insert(:group)
|
||||||
|
|
||||||
|
post_data =
|
||||||
|
Map.merge(@valid_attrs, %{
|
||||||
|
author_id: actor.id,
|
||||||
|
attributed_to_id: group.id,
|
||||||
|
url: "https://remote.tld/p/post"
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, %Post{} = _post} = Posts.create_post(post_data)
|
||||||
|
|
||||||
|
assert {:error,
|
||||||
|
%Ecto.Changeset{
|
||||||
|
errors: [
|
||||||
|
url:
|
||||||
|
{"has already been taken",
|
||||||
|
[constraint: :unique, constraint_name: "posts_url_index"]}
|
||||||
|
]
|
||||||
|
}} = Posts.create_post(post_data)
|
||||||
|
end
|
||||||
|
|
||||||
test "create_post/1 with invalid data returns error changeset" do
|
test "create_post/1 with invalid data returns error changeset" do
|
||||||
assert {:error, %Ecto.Changeset{}} = Posts.create_post(@invalid_attrs)
|
assert {:error, %Ecto.Changeset{}} = Posts.create_post(@invalid_attrs)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue