Skip to content

Commit

Permalink
Add activate user task (#556)
Browse files Browse the repository at this point in the history
  • Loading branch information
michelletran-codecov committed Jul 17, 2024
1 parent e78491c commit e9abbde
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 5 deletions.
2 changes: 1 addition & 1 deletion requirements.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
https://github.com/codecov/shared/archive/3b44680b87559e7fe69b3e9645172a99f6bdbf98.tar.gz#egg=shared
https://github.com/codecov/shared/archive/03dc0857411eb062a8ca759ca82da8ba6b43aa82.tar.gz#egg=shared
https://github.com/codecov/opentelem-python/archive/refs/tags/v0.0.4a1.tar.gz#egg=codecovopentelem
https://github.com/codecov/test-results-parser/archive/1507de2241601d678e514c08b38426e48bb6d47d.tar.gz#egg=test-results-parser
boto3>=1.34
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ sentry-sdk==1.40.0
# via
# -r requirements.in
# shared
shared @ https://github.com/codecov/shared/archive/3b44680b87559e7fe69b3e9645172a99f6bdbf98.tar.gz
shared @ https://github.com/codecov/shared/archive/03dc0857411eb062a8ca759ca82da8ba6b43aa82.tar.gz
# via -r requirements.in
six==1.16.0
# via
Expand Down
1 change: 1 addition & 0 deletions tasks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# ruff: noqa: F401
from app import celery_app
from tasks.activate_account_user import activate_account_user_task
from tasks.ai_pr_review import ai_pr_view_task
from tasks.backfill_commit_data_to_storage import backfill_commit_data_to_storage_task
from tasks.backfill_existing_gh_app_installations import (
Expand Down
61 changes: 61 additions & 0 deletions tasks/activate_account_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import logging

from shared.celery_config import activate_account_user_task_name
from shared.django_apps.codecov_auth.models import Account, Owner

from app import celery_app
from tasks.base import BaseCodecovTask

log = logging.getLogger(__name__)


class ActivateAccountUserTask(BaseCodecovTask, name=activate_account_user_task_name):
def run_impl(
self,
*,
user_ownerid: int,
org_ownerid: int,
**kwargs,
):
"""
Runs the task to activate a user onto an account.
:param user_ownerid: the user's owner id
:param org_ownerid: the organization owner id
"""
log_context = {"user_ownerid": user_ownerid, "org_ownerid": org_ownerid}
log.info(
"Syncing account for user",
extra=log_context,
)

owner_user: Owner = Owner.objects.get(pk=user_ownerid)

# NOTE: We're currently ignoring organizations that don't have an account.
org_owner: Owner = Owner.objects.get(pk=org_ownerid)
account: Account | None = org_owner.account
if not account:
log.info(
"Organization does not have an account. Skipping account user activation."
)
return {"successful": True}

if account.can_activate_user(owner_user.user):
account.activate_owner_user_onto_account(owner_user)
account.save()
else:
log.info(
"User was not able to activate on account. It could be that the user is already activated, "
"or the account is in an inconsistent state.",
extra=log_context,
)

log.info(
"Successfully synced account for user",
extra=log_context,
)

return {"successful": True}


RegisteredActivateAccountUserTask = celery_app.register_task(ActivateAccountUserTask())
activate_account_user_task = celery_app.tasks[ActivateAccountUserTask.name]
8 changes: 8 additions & 0 deletions tasks/notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from asgiref.sync import async_to_sync
from celery.exceptions import MaxRetriesExceededError, SoftTimeLimitExceeded
from shared.celery_config import (
activate_account_user_task_name,
new_user_activated_task_name,
notify_task_name,
status_set_error_task_name,
Expand Down Expand Up @@ -678,6 +679,13 @@ def schedule_new_user_activated_task(self, org_ownerid, user_ownerid):
args=None,
kwargs=dict(org_ownerid=org_ownerid, user_ownerid=user_ownerid),
)
# Activate the account user if it exists.
self.app.tasks[activate_account_user_task_name].apply_async(
kwargs=dict(
user_ownerid=user_ownerid,
org_ownerid=org_ownerid,
),
)

def fetch_and_update_whether_ci_passed(
self, repository_service, commit, current_yaml
Expand Down
84 changes: 84 additions & 0 deletions tasks/tests/unit/test_activate_account_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import pytest
from shared.django_apps.codecov_auth.models import AccountsUsers
from shared.django_apps.codecov_auth.tests.factories import (
AccountFactory,
OwnerFactory,
UserFactory,
)

from tasks.activate_account_user import ActivateAccountUserTask


@pytest.mark.django_db
def test_activate_account_user_skip_no_account(caplog):
user = OwnerFactory()
org = OwnerFactory()
org.account = None
ActivateAccountUserTask().run_impl(
user_ownerid=user.ownerid, org_ownerid=org.ownerid
)
assert len(caplog.records) == 2
assert (
caplog.records[1].message
== "Organization does not have an account. Skipping account user activation."
)


@pytest.mark.parametrize(
"plan_seat_count,free_seat_count,is_user_student,expected_user_count",
[
pytest.param(0, 0, False, 0, id="cannot_activate_no_seats_available"),
pytest.param(1, 0, False, 1, id="activate_with_seats_available"),
pytest.param(0, 1, False, 1, id="activate_with_free_seats_available"),
pytest.param(2, 0, True, 1, id="activate_github_student"),
pytest.param(2, 1, True, 1, id="activate_github_student"),
],
)
@pytest.mark.django_db
def test_activate_account_user(
plan_seat_count: int,
free_seat_count: int,
is_user_student: bool,
expected_user_count: int,
):
user = OwnerFactory()
user.student = is_user_student
user.user = UserFactory()
org = OwnerFactory()
account = AccountFactory(
plan_seat_count=plan_seat_count, free_seat_count=free_seat_count
)
org.account = account
org.save()
assert AccountsUsers.objects.count() == 0

ActivateAccountUserTask().run_impl(
user_ownerid=user.ownerid, org_ownerid=org.ownerid
)
assert AccountsUsers.objects.count() == expected_user_count
if expected_user_count > 0:
assert AccountsUsers.objects.first().account == account
assert AccountsUsers.objects.first().user.owners.first() == user


@pytest.mark.django_db
def test_activate_account_user_already_exists():
user = OwnerFactory()
user.user = UserFactory()
org = OwnerFactory()
account = AccountFactory()
org.account = account
org.save()

account.users.add(user.user)
account.save()
user.save()

assert AccountsUsers.objects.filter(account=account, user=user.user).count() == 1

ActivateAccountUserTask().run_impl(
user_ownerid=user.ownerid, org_ownerid=org.ownerid
)

# Nothing happens... user already exists.
assert AccountsUsers.objects.filter(account=account, user=user.user).count() == 1
20 changes: 17 additions & 3 deletions tasks/tests/unit/test_notify_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
import pytest
from celery.exceptions import MaxRetriesExceededError, Retry
from freezegun import freeze_time
from shared.celery_config import new_user_activated_task_name
from shared.celery_config import (
activate_account_user_task_name,
new_user_activated_task_name,
)
from shared.reports.resources import Report
from shared.torngit.base import TorngitBaseAdapter
from shared.torngit.exceptions import (
Expand Down Expand Up @@ -235,15 +238,26 @@ def test_determine_decoration_type_from_pull_attempt_activation(
enriched_pull.database_pull.repository.owner.ownerid,
pr_author.ownerid,
)
assert mocked_send_task.call_count == 1
mocked_send_task.assert_called_with(
assert mocked_send_task.call_count == 2

new_user_activation_call = mocked_send_task.call_args_list[0]
account_user_activation_call = mocked_send_task.call_args_list[1]
assert new_user_activation_call == call(
new_user_activated_task_name,
args=None,
kwargs=dict(
org_ownerid=enriched_pull.database_pull.repository.owner.ownerid,
user_ownerid=pr_author.ownerid,
),
)
assert account_user_activation_call[0] == (
activate_account_user_task_name,
None,
{
"org_ownerid": enriched_pull.database_pull.repository.owner.ownerid,
"user_ownerid": pr_author.ownerid,
},
)

@pytest.mark.parametrize("cached_id, app_to_save", [("24", "24"), (None, 12)])
def test__possibly_refresh_previous_selection(
Expand Down

0 comments on commit e9abbde

Please sign in to comment.