Skip to content
Open
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
31 changes: 30 additions & 1 deletion invenio_rdm_records/auditlog/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
Grant,
)

from .context import LogChangesContext, ResourceDataContext
from .context import FileContext, LogChangesContext, ResourceDataContext


class ParentBaseAuditLog(BaseAuditLog):
Expand Down Expand Up @@ -128,3 +128,32 @@ class RDMDraftAccessSettingsAuditLog(RDMRecordAccessSettingsAuditLog):
message_template = _(
"User {user_id} updated access settings of {resource_type} {resource_id} via draft."
)


class FileCreateAuditLog(BaseAuditLog):
"""Audit log for file create."""

resource_type = "draft"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this log only for the initial file creation? What if a record is modified and files are added?

Copy link
Copy Markdown
Contributor Author

@sakshamarora1 sakshamarora1 Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then it's a new file and a new audit log entry, through the FileService commit_file method


context = BaseAuditLog.context + [
FileContext(),
]

id = "file.create"
message_template = _(
"User {user_id} created file {file_key} of {resource_type} {resource_id}."
)

metadata_schema = {
**BaseAuditLog.metadata_schema,
"file_key": ma.fields.String(required=True),
}


class FileDeleteAuditLog(FileCreateAuditLog):
"""Audit log for file delete."""

id = "file.delete"
message_template = _(
"User {user_id} deleted file {file_key} of {resource_type} {resource_id}."
)
9 changes: 9 additions & 0 deletions invenio_rdm_records/auditlog/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,12 @@ def __call__(self, data, **kwargs):
"type": "draft" if isinstance(resource, RDMDraft) else "record",
}
dict_set(data, "metadata.triggered_by", triggered_by)


class FileContext(object):
"""Payload generator for setting file data."""

def __call__(self, data, **kwargs):
"""Update data with file data."""
file_key = kwargs.get("file_key", None)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit; the marshmallow schema is required, but here it can possibly be None

dict_set(data, "metadata.file_key", file_key)
26 changes: 26 additions & 0 deletions invenio_rdm_records/services/files/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@

"""File Service API."""

from invenio_audit_logs.services.uow import AuditLogOp
from invenio_records_resources.services import FileService
from invenio_records_resources.services.uow import unit_of_work

from invenio_rdm_records.auditlog.actions import (
FileCreateAuditLog,
FileDeleteAuditLog,
)
from invenio_rdm_records.services.errors import RecordDeletedException


Expand All @@ -32,3 +38,23 @@ def _get_record(self, id_, identity, action, file_key=None):
self._check_record_deleted_permissions(record, identity)

return record

@unit_of_work()
def commit_file(self, identity, id_, file_key, uow=None):
"""Commit a file upload."""
result = super().commit_file(identity, id_, file_key, uow=uow)

uow.register(
AuditLogOp(FileCreateAuditLog.build(identity, id_, file_key=file_key))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we choose to use the drafts id for file creation logs and not the parent.pid.pid_value as we do In the other logs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the share access logs, the metadata of the parent is updated. Here, the metadata of the files attached to the record are updated. And through the UI, we can only create or delete a file.

) # Added here as audit logs can't be added to invenio-records-resources
return result

@unit_of_work()
def delete_file(self, identity, id_, file_key, uow=None):
"""Delete a file."""
result = super().delete_file(identity, id_, file_key, uow=uow)

uow.register(
AuditLogOp(FileDeleteAuditLog.build(identity, id_, file_key=file_key))
) # Added here as audit logs can't be added to invenio-records-resources
return result
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ invenio_audit_logs.actions =
draft.secret_link_update = invenio_rdm_records.auditlog.actions:RDMDraftSecretLinkAuditLog
record.access_settings_update = invenio_rdm_records.auditlog.actions:RDMRecordAccessSettingsAuditLog
draft.access_settings_update = invenio_rdm_records.auditlog.actions:RDMDraftAccessSettingsAuditLog
file.create = invenio_rdm_records.auditlog.actions:FileCreateAuditLog
file.delete = invenio_rdm_records.auditlog.actions:FileDeleteAuditLog

[build_sphinx]
source-dir = docs/
Expand Down
Loading