Skip to content

Commit

Permalink
Add audit events for dandiset stars
Browse files Browse the repository at this point in the history
  • Loading branch information
jjnesbitt committed Jan 3, 2025
1 parent 0d1aace commit 413e4ad
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 9 deletions.
31 changes: 26 additions & 5 deletions dandiapi/api/migrations/0014_dandisetstar.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 4.2.17 on 2024-12-26 14:24
# Generated by Django 4.2.17 on 2025-01-03 21:07
from __future__ import annotations

from django.conf import settings
Expand All @@ -13,16 +13,37 @@ class Migration(migrations.Migration):
]

operations = [
migrations.AlterField(
model_name='auditrecord',
name='record_type',
field=models.CharField(
choices=[
('create_dandiset', 'create_dandiset'),
('change_owners', 'change_owners'),
('update_metadata', 'update_metadata'),
('add_asset', 'add_asset'),
('update_asset', 'update_asset'),
('remove_asset', 'remove_asset'),
('create_zarr', 'create_zarr'),
('upload_zarr_chunks', 'upload_zarr_chunks'),
('delete_zarr_chunks', 'delete_zarr_chunks'),
('finalize_zarr', 'finalize_zarr'),
('unembargo_dandiset', 'unembargo_dandiset'),
('publish_dandiset', 'publish_dandiset'),
('star_dandiset', 'star_dandiset'),
('unstar_dandiset', 'unstar_dandiset'),
('delete_dandiset', 'delete_dandiset'),
],
max_length=32,
),
),
migrations.CreateModel(
name='DandisetStar',
fields=[
(
'id',
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID',
auto_created=True, primary_key=True, serialize=False, verbose_name='ID'
),
),
('created', models.DateTimeField(auto_now_add=True)),
Expand Down
2 changes: 2 additions & 0 deletions dandiapi/api/models/audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
'finalize_zarr',
'unembargo_dandiset',
'publish_dandiset',
'star_dandiset',
'unstar_dandiset',
'delete_dandiset',
]
AUDIT_RECORD_CHOICES = [(t, t) for t in get_args(AuditRecordType)]
Expand Down
22 changes: 22 additions & 0 deletions dandiapi/api/services/audit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,28 @@ def publish_dandiset(*, dandiset: Dandiset, user: User, version: str) -> AuditRe
)


def star_dandiset(*, dandiset: Dandiset, user: User, new_star_count: int) -> AuditRecord:
return _make_audit_record(
dandiset=dandiset,
user=user,
record_type='star_dandiset',
details={
'new_star_count': new_star_count,
},
)


def unstar_dandiset(*, dandiset: Dandiset, user: User, new_star_count: int) -> AuditRecord:
return _make_audit_record(
dandiset=dandiset,
user=user,
record_type='unstar_dandiset',
details={
'new_star_count': new_star_count,
},
)


def delete_dandiset(*, dandiset: Dandiset, user: User):
return _make_audit_record(
dandiset=dandiset, user=user, record_type='delete_dandiset', details={}
Expand Down
16 changes: 12 additions & 4 deletions dandiapi/api/services/dandiset/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,12 @@ def star_dandiset(*, user, dandiset: Dandiset) -> int:
if not user.is_authenticated:
return dandiset.star_count

DandisetStar.objects.get_or_create(user=user, dandiset=dandiset)
return dandiset.star_count
with transaction.atomic():
DandisetStar.objects.get_or_create(user=user, dandiset=dandiset)
new_star_count = dandiset.star_count
audit.star_dandiset(dandiset=dandiset, user=user, new_star_count=new_star_count)

return new_star_count


def unstar_dandiset(*, user, dandiset: Dandiset) -> int:
Expand All @@ -108,5 +112,9 @@ def unstar_dandiset(*, user, dandiset: Dandiset) -> int:
if not user.is_authenticated:
return dandiset.star_count

DandisetStar.objects.filter(user=user, dandiset=dandiset).delete()
return dandiset.star_count
with transaction.atomic():
DandisetStar.objects.filter(user=user, dandiset=dandiset).delete()
new_star_count = dandiset.star_count
audit.unstar_dandiset(dandiset=dandiset, user=user, new_star_count=new_star_count)

return new_star_count
19 changes: 19 additions & 0 deletions dandiapi/api/tests/test_dandiset.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from dandiapi.api.asset_paths import add_asset_paths, add_version_asset_paths
from dandiapi.api.models import Dandiset, Version
from dandiapi.api.models.audit import AuditRecord

from .fuzzy import DANDISET_ID_RE, DANDISET_SCHEMA_ID_RE, TIMESTAMP_RE, UTC_ISO_TIMESTAMP_RE

Expand Down Expand Up @@ -1230,12 +1231,23 @@ def test_dandiset_rest_clear_active_uploads(
@pytest.mark.django_db
def test_dandiset_star(api_client, user, dandiset):
api_client.force_authenticate(user=user)
assert not AuditRecord.objects.filter(record_type='star_dandiset').exists()
assert not dandiset.stars.exists()

response = api_client.post(f'/api/dandisets/{dandiset.identifier}/star/')
assert response.status_code == 200
assert response.data == {'count': 1}

# Check for star db entry
assert dandiset.stars.count() == 1
assert dandiset.stars.first().user == user

# Check for audit record
assert AuditRecord.objects.filter(record_type='star_dandiset').count() == 1
audit_record = AuditRecord.objects.get(record_type='star_dandiset')
assert audit_record.dandiset_id == dandiset.id
assert audit_record.username == user.username


@pytest.mark.django_db
def test_dandiset_unstar(api_client, user, dandiset):
Expand All @@ -1250,6 +1262,13 @@ def test_dandiset_unstar(api_client, user, dandiset):
assert response.data == {'count': 0}
assert dandiset.stars.count() == 0

# Check for audit records
assert AuditRecord.objects.filter(dandiset_id=dandiset.id).count() == 2
assert AuditRecord.objects.filter(record_type='unstar_dandiset').count() == 1
audit_record = AuditRecord.objects.get(record_type='unstar_dandiset')
assert audit_record.dandiset_id == dandiset.id
assert audit_record.username == user.username


@pytest.mark.django_db
def test_dandiset_star_unauthenticated(api_client, dandiset):
Expand Down

0 comments on commit 413e4ad

Please sign in to comment.