Skip to content

Commit 273f72d

Browse files
committed
Add triggers for new addons and metadata change to submit to Cinder
1 parent 094de10 commit 273f72d

8 files changed

Lines changed: 222 additions & 43 deletions

File tree

src/olympia/abuse/tasks.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import olympia.core.logger
1313
from olympia import amo
1414
from olympia.activity.models import ActivityLog
15-
from olympia.addons.models import Addon
15+
from olympia.addons.models import Addon, AddonApprovalsCounter
1616
from olympia.amo.celery import task
1717
from olympia.amo.decorators import use_primary_db
1818
from olympia.amo.utils import to_language
@@ -287,6 +287,9 @@ def submit_addon_for_content_review(*, addon_pk):
287287
addon = Addon.unfiltered.get(pk=addon_pk)
288288
entity_helper = CinderAddonContentReview(addon)
289289
entity_helper.send_event()
290+
AddonApprovalsCounter.reset_content_for_addon(
291+
addon, initial_status=AddonApprovalsCounter.CONTENT_REVIEW_STATUSES.PENDING
292+
)
290293

291294
# Additional context is submitted as separate api calls.
292295
try:

src/olympia/abuse/tests/test_tasks.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from olympia import amo
1515
from olympia.activity.models import ActivityLog
16+
from olympia.addons.models import AddonApprovalsCounter
1617
from olympia.amo.tests import TestCase, addon_factory, days_ago, user_factory
1718
from olympia.constants.abuse import (
1819
DECISION_ACTIONS,
@@ -1336,6 +1337,11 @@ def test_submit_addon_for_content_review():
13361337
additional_call = json.loads(responses.calls[1].request.body)
13371338
assert additional_call['entities'][0]['attributes']['id'] == str(author2.id)
13381339

1340+
assert (
1341+
AddonApprovalsCounter.objects.get(addon=addon).content_review_status
1342+
== AddonApprovalsCounter.CONTENT_REVIEW_STATUSES.PENDING
1343+
)
1344+
13391345

13401346
@pytest.mark.django_db
13411347
def test_submit_addon_change_for_content_review():

src/olympia/addons/models.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,8 @@ def not_disabled_by_mozilla(self):
262262

263263
def get_base_queryset_for_queue(
264264
self,
265+
*,
265266
admin_reviewer=False,
266-
content_review=False,
267267
theme_review=False,
268268
select_related_fields_for_listed=True,
269269
):
@@ -392,9 +392,7 @@ def get_queryset_for_pending_queues(
392392
def get_content_review_queue(self, admin_reviewer=False):
393393
"""Return a queryset of Addon objects that need content review."""
394394
qs = (
395-
self.get_base_queryset_for_queue(
396-
admin_reviewer=admin_reviewer, content_review=True
397-
)
395+
self.get_base_queryset_for_queue(admin_reviewer=admin_reviewer)
398396
.valid()
399397
.filter(
400398
_current_version__reviewerflags__pending_rejection__isnull=True,
@@ -2108,6 +2106,8 @@ def watch_status(old_attr=None, new_attr=None, instance=None, sender=None, **kwa
21082106
If a version is rejected after nomination, the developer has to upload a
21092107
new version.
21102108
"""
2109+
from olympia.abuse.tasks import submit_addon_for_content_review
2110+
21112111
new_status = new_attr.get('status') if new_attr else None
21122112
old_status = old_attr.get('status') if old_attr else None
21132113
disabled_by_user = new_attr.get('disabled_by_user') if new_attr else None
@@ -2131,7 +2131,20 @@ def watch_status(old_attr=None, new_attr=None, instance=None, sender=None, **kwa
21312131
status_disabled_reason=File.STATUS_DISABLED_REASONS.DEVELOPER,
21322132
)
21332133
instance.update_status()
2134-
elif old_status == amo.STATUS_NOMINATED:
2134+
return
2135+
2136+
if (
2137+
new_status in amo.VALID_ADDON_STATUSES
2138+
and waffle.switch_is_active('content-review-in-cinder')
2139+
and not AddonApprovalsCounter.objects.filter(addon_id=instance.id)
2140+
.exclude(
2141+
content_review_status=AddonApprovalsCounter.CONTENT_REVIEW_STATUSES.UNREVIEWED
2142+
)
2143+
.exists()
2144+
):
2145+
submit_addon_for_content_review.delay(addon_pk=instance.pk)
2146+
2147+
if old_status == amo.STATUS_NOMINATED:
21352148
# Update latest version due date if necessary for nominated add-ons.
21362149
inherit_due_date_if_nominated(None, latest_version)
21372150
else:
@@ -2427,11 +2440,10 @@ class CONTENT_REVIEW_STATUSES(EnumChoices):
24272440
PASS = 2, 'Pass'
24282441
FAIL = 3, 'Fail'
24292442
REQUESTED = 4, 'Pending, New review requested'
2443+
# This status is set when the review is sent to Cinder
2444+
PENDING = 5, 'Pending first review after submission'
24302445

24312446
CONTENT_REVIEW_STATUSES.add_subset('REJECTED', ('FAIL', 'REQUESTED'))
2432-
CONTENT_REVIEW_STATUSES.add_subset(
2433-
'PENDING', ('UNREVIEWED', 'CHANGED', 'REQUESTED')
2434-
)
24352447
CONTENT_REVIEW_STATUSES.add_subset('COMPLETE', ('PASS', 'FAIL'))
24362448

24372449
addon = models.OneToOneField(Addon, primary_key=True, on_delete=models.CASCADE)
@@ -2516,13 +2528,18 @@ def reject_content_for_addon(cls, addon):
25162528
return obj
25172529

25182530
@classmethod
2519-
def reset_content_for_addon(cls, addon):
2531+
def reset_content_for_addon(
2532+
cls, addon, initial_status=CONTENT_REVIEW_STATUSES.UNREVIEWED
2533+
):
25202534
"""
25212535
Reset the last_content_review date for this addon so it triggers
25222536
another review.
25232537
"""
2538+
defaults = {'last_content_review': None}
25242539
obj, created = cls.objects.update_or_create(
2525-
addon=addon, defaults={'last_content_review': None}
2540+
addon=addon,
2541+
defaults=defaults,
2542+
create_defaults={**defaults, 'content_review_status': initial_status},
25262543
)
25272544
if (
25282545
not created

src/olympia/addons/serializers.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1319,6 +1319,8 @@ def _save_icon(self, uploaded_icon):
13191319
self.instance.update(icon_type='')
13201320

13211321
def log(self, instance, validated_data, changes):
1322+
from olympia.abuse.tasks import submit_addon_change_for_content_review
1323+
13221324
validated_data = {**validated_data} # we want to modify it, so take a copy
13231325
user = self.context['request'].user
13241326

@@ -1340,13 +1342,15 @@ def log(self, instance, validated_data, changes):
13401342
validated_data.pop('version')
13411343

13421344
for field, addedremoved in changes.items():
1343-
ActivityLog.objects.create(
1345+
alog = ActivityLog.objects.create(
13441346
amo.LOG.EDIT_ADDON_PROPERTY,
13451347
instance,
13461348
field,
13471349
json.dumps(addedremoved),
13481350
user=user,
13491351
)
1352+
if waffle.switch_is_active('content-review-in-cinder'):
1353+
submit_addon_change_for_content_review.delay(activity_log_pk=alog.pk)
13501354
validated_data.pop(field)
13511355
if validated_data:
13521356
ActivityLog.objects.create(

src/olympia/addons/tests/test_models.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2964,6 +2964,56 @@ def test_rejected_listing_addons_do_not_update(self):
29642964
addon.update_status()
29652965
assert addon.status == amo.STATUS_APPROVED
29662966

2967+
@patch('olympia.abuse.tasks.submit_addon_for_content_review.delay')
2968+
@override_switch('content-review-in-cinder', active=True)
2969+
def test_content_review_triggered_for_addon_submission(self, submit_mock):
2970+
addon = addon_factory()
2971+
submit_mock.reset_mock()
2972+
addon.update(status=amo.STATUS_NOMINATED)
2973+
submit_mock.assert_called_once_with(addon_pk=addon.pk)
2974+
2975+
@patch('olympia.abuse.tasks.submit_addon_for_content_review.delay')
2976+
@override_switch('content-review-in-cinder', active=False)
2977+
def test_content_review_not_triggered_when_waffle_not_enabled(self, submit_mock):
2978+
addon = addon_factory()
2979+
submit_mock.reset_mock()
2980+
addon.update(status=amo.STATUS_NOMINATED)
2981+
submit_mock.assert_not_called()
2982+
2983+
@patch('olympia.abuse.tasks.submit_addon_for_content_review.delay')
2984+
@override_switch('content-review-in-cinder', active=True)
2985+
def test_content_review_not_triggered_for_non_valid_status(self, submit_mock):
2986+
addon = addon_factory()
2987+
submit_mock.reset_mock()
2988+
addon.update(status=amo.STATUS_DISABLED)
2989+
submit_mock.assert_not_called()
2990+
2991+
@patch('olympia.abuse.tasks.submit_addon_for_content_review.delay')
2992+
@override_switch('content-review-in-cinder', active=True)
2993+
def test_content_review_not_triggered_when_already_pending(self, submit_mock):
2994+
addon = addon_factory()
2995+
AddonApprovalsCounter.objects.create(
2996+
addon=addon,
2997+
content_review_status=AddonApprovalsCounter.CONTENT_REVIEW_STATUSES.PENDING,
2998+
)
2999+
submit_mock.reset_mock()
3000+
addon.update(status=amo.STATUS_NOMINATED)
3001+
submit_mock.assert_not_called()
3002+
3003+
@patch('olympia.abuse.tasks.submit_addon_for_content_review.delay')
3004+
@override_switch('content-review-in-cinder', active=True)
3005+
def test_content_review_not_triggered_when_content_already_reviewed(
3006+
self, submit_mock
3007+
):
3008+
addon = addon_factory()
3009+
AddonApprovalsCounter.objects.create(
3010+
addon=addon,
3011+
content_review_status=AddonApprovalsCounter.CONTENT_REVIEW_STATUSES.PASS,
3012+
)
3013+
submit_mock.reset_mock()
3014+
addon.update(status=amo.STATUS_NOMINATED)
3015+
submit_mock.assert_not_called()
3016+
29673017

29683018
class TestGetVersion(TestCase):
29693019
fixtures = [
@@ -3750,6 +3800,27 @@ def test_reset_content_existing(self):
37503800
AddonApprovalsCounter.reset_content_for_addon(self.addon)
37513801
assert approval_counter.reload().content_review_status == previous_status
37523802

3803+
def test_reset_content_existing_initial_status_ignored(self):
3804+
approval_counter = AddonApprovalsCounter.objects.create(
3805+
addon=self.addon,
3806+
counter=42,
3807+
last_content_review=self.days_ago(367),
3808+
last_human_review=self.days_ago(10),
3809+
content_review_status=AddonApprovalsCounter.CONTENT_REVIEW_STATUSES.PASS,
3810+
)
3811+
# should be ignored
3812+
AddonApprovalsCounter.reset_content_for_addon(
3813+
self.addon,
3814+
initial_status=AddonApprovalsCounter.CONTENT_REVIEW_STATUSES.REQUESTED,
3815+
)
3816+
assert approval_counter.reload().counter == 42 # unchanged
3817+
self.assertCloseToNow(approval_counter.last_human_review, now=self.days_ago(10))
3818+
assert approval_counter.last_content_review is None
3819+
assert (
3820+
approval_counter.content_review_status
3821+
== AddonApprovalsCounter.CONTENT_REVIEW_STATUSES.CHANGED
3822+
)
3823+
37533824
def test_reset_content_non_existing(self):
37543825
assert not AddonApprovalsCounter.objects.filter(addon=self.addon).exists()
37553826
AddonApprovalsCounter.reset_content_for_addon(self.addon)
@@ -3761,6 +3832,20 @@ def test_reset_content_non_existing(self):
37613832
== AddonApprovalsCounter.CONTENT_REVIEW_STATUSES.UNREVIEWED
37623833
)
37633834

3835+
def test_reset_content_non_existing_with_initial_status(self):
3836+
assert not AddonApprovalsCounter.objects.filter(addon=self.addon).exists()
3837+
AddonApprovalsCounter.reset_content_for_addon(
3838+
self.addon,
3839+
initial_status=AddonApprovalsCounter.CONTENT_REVIEW_STATUSES.PENDING,
3840+
)
3841+
approval_counter = AddonApprovalsCounter.objects.get(addon=self.addon)
3842+
assert approval_counter.counter == 0
3843+
assert approval_counter.last_human_review is None
3844+
assert (
3845+
approval_counter.content_review_status
3846+
== AddonApprovalsCounter.CONTENT_REVIEW_STATUSES.PENDING
3847+
)
3848+
37643849
def test_request_review(self):
37653850
AddonApprovalsCounter.request_new_content_review_for_addon(self.addon)
37663851
approval_counter = AddonApprovalsCounter.objects.get(addon=self.addon)

0 commit comments

Comments
 (0)