Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
50 changes: 13 additions & 37 deletions tcms_tenants/management/commands/remove_stale_tenant_comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,22 @@
# Licensed under GNU Affero General Public License v3 or later (AGPLv3+)
# https://www.gnu.org/licenses/agpl-3.0.html

from django.core.management.base import BaseCommand
from django.utils import timezone
from django_tenants.utils import get_tenant_model, tenant_context
from django_comments.models import Comment

from django_comments.management.commands.delete_stale_comments import (
Command as DeleteStaleCommentsCommand,
)

class Command(BaseCommand):

class Command(DeleteStaleCommentsCommand):
help = (
"Remove comments for which the related objects don't exist anymore! "
"Works on all tenants!"
)

def add_arguments(self, parser):
parser.add_argument(
"-y",
"--yes",
default="x",
action="store_const",
const="y",
dest="answer",
help="Automatically confirm deletion",
)
super().add_arguments(parser)
parser.add_argument(
"--dry-run",
action="store_true",
Expand All @@ -33,13 +27,17 @@ def add_arguments(self, parser):
)

def handle(self, *args, **kwargs):
answer = kwargs["answer"]
dry_run = kwargs["dry_run"]

output = None
if kwargs["verbosity"]:
output = self.stdout

if dry_run:
# the upstream command reports each match before prompting, so
# refusing to delete turns it into a dry run
kwargs["answer"] = "n"

for tenant in get_tenant_model().objects.all():
if output:
output.write(
Expand All @@ -48,30 +46,8 @@ def handle(self, *args, **kwargs):
)

with tenant_context(tenant):
for comment in Comment.objects.all():
if not comment.object_pk or comment.content_object is None:
if output:
output.write(
f"Comment `{comment}' to non-existing "
f"`{comment.content_type.model}' with PK "
f"`{comment.object_pk}'"
)

if dry_run:
continue

while answer not in "yn":
answer = input("Do you wish to delete? [yN] ")
if not answer:
answer = "x"
continue
answer = answer[0].lower()

if answer == "y":
comment.delete()

if output:
output.write(f"Deleted comment `{comment}'")
# upstream removes comments whose related object is gone
super().handle(*args, **kwargs)

if output:
output.write(
Expand Down
49 changes: 39 additions & 10 deletions tcms_tenants/tests/test_remove_stale_tenant_comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,38 @@ def add_comments(cls, parent_obj):
stale.object_pk = str(int(parent_obj.pk) + cls.pk_offset)
stale.save()

stale2 = add_comment(
[parent_obj], "comment with object_pk=''", cls.tenant.owner
)[0]
stale2.object_pk = ""
stale2.save()
def test_dry_run_removes_nothing(self):
tenants = tenants_with_schema()
self.assertGreaterEqual(len(tenants), 1)

out = StringIO()
call_command(
"remove_stale_tenant_comments",
answer="y",
dry_run=True,
verbosity=1,
stdout=out,
)

output = out.getvalue()

for tenant in tenants:
with tenant_context(tenant):
self.assertEqual(
Comment.objects.count(), self.comments_per_model * 2 * 3
)
self.assertTrue(
Comment.objects.annotate(
object_pk_as_int=Cast("object_pk", IntegerField())
)
.filter(object_pk_as_int__gt=self.pk_offset)
.exists()
)

self.assertIn(
f"for tenant '{tenant.schema_name}' === dry run: True ===",
output,
)

def test_removes_only_comments_which_are_attached_to_missing_objects(self):
tenants = tenants_with_schema()
Expand All @@ -69,13 +96,14 @@ def test_removes_only_comments_which_are_attached_to_missing_objects(self):
for tenant in tenants:
with tenant_context(tenant):
self.assertTrue(
Comment.objects.exclude(object_pk="")
.annotate(object_pk_as_int=Cast("object_pk", IntegerField()))
Comment.objects.annotate(
object_pk_as_int=Cast("object_pk", IntegerField())
)
.filter(object_pk_as_int__gt=self.pk_offset)
.exists()
)
self.assertEqual(
Comment.objects.count(), self.comments_per_model * 3 * 3
Comment.objects.count(), self.comments_per_model * 2 * 3
)

out = StringIO()
Expand All @@ -92,8 +120,9 @@ def test_removes_only_comments_which_are_attached_to_missing_objects(self):
with tenant_context(tenant):
self.assertEqual(Comment.objects.count(), self.comments_per_model * 3)
self.assertFalse(
Comment.objects.exclude(object_pk="")
.annotate(object_pk_as_int=Cast("object_pk", IntegerField()))
Comment.objects.annotate(
object_pk_as_int=Cast("object_pk", IntegerField())
)
.filter(object_pk_as_int__gt=self.pk_offset)
.exists()
)
Expand Down
Loading