Skip to content

Commit

Permalink
Improve logging and error messages (#134)
Browse files Browse the repository at this point in the history
  • Loading branch information
super-cooper authored Sep 1, 2023
1 parent 5cdada7 commit 5aa90a1
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 10 deletions.
5 changes: 5 additions & 0 deletions trovi/api/patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ def apply(self, obj: dict[str, JSON], in_place: bool = False) -> dict[str, JSON]
return updated_fields

def _get_operation(self, operation: JSONObject) -> jsonpatch.PatchOperation:
"""
This override associates an ID with each operation object so that the
owner of the object is able to aggregate errors from all the operations
it owns.
"""
try:
op = super(ArtifactPatch, self)._get_operation(operation)
except jsonpatch.InvalidJsonPatch as e:
Expand Down
18 changes: 15 additions & 3 deletions trovi/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,8 @@ def update(
# Because of strict_schema validation, this should be unreachable
raise ValueError("Unknown event type")

LOG.info(f"Recording {count} {event_type} event(s) from {origin}")

# We don't call bulk_create here, because it doesn't run any save signals
# even though it persists the models to the database
with transaction.atomic():
Expand Down Expand Up @@ -376,6 +378,10 @@ def create(self, validated_data: dict) -> ArtifactVersion:
contents_serializer.is_valid(raise_exception=True)
contents_serializer.save()

view = self.context["view"]
artifact_uuid = view.kwargs.get("parent_lookup_artifact")
LOG.info(f"Created new version for artifact {artifact_uuid}: {version.slug}")

return version

def to_representation(self, instance: ArtifactVersion) -> dict:
Expand All @@ -402,7 +408,9 @@ class Meta:

def create(self, validated_data: dict) -> ArtifactRole:
validated_data["assigned_by"] = get_requesting_user_urn(self)
return super(ArtifactRoleSerializer, self).create(validated_data)
role = super(ArtifactRoleSerializer, self).create(validated_data)
LOG.info(f"New role: {role.user} as {role.role} on {role.artifact.uuid}")
return role

def update(self, instance, validated_data: dict):
raise NotImplementedError(f"Incorrect use of {self.__class__.__name__}")
Expand Down Expand Up @@ -581,6 +589,8 @@ def create(self, validated_data: dict) -> Artifact:
role=ArtifactRole.RoleType.ADMINISTRATOR,
)

LOG.info(f"Created new artifact: {artifact.uuid}")

return artifact

def update(self, instance: Artifact, validated_data: dict) -> Artifact:
Expand Down Expand Up @@ -653,7 +663,9 @@ def update(self, instance: Artifact, validated_data: dict) -> Artifact:
role=ArtifactRole.RoleType.ADMINISTRATOR,
)

return super(ArtifactSerializer, self).update(instance, validated_data)
updated = super(ArtifactSerializer, self).update(instance, validated_data)
LOG.info(f"Updated artifact {updated.uuid}: {', '.join(validated_data)}")
return updated

def to_internal_value(self, data: dict) -> dict:
# If this is a new Artifact, its default owner is the user who is creating it
Expand Down Expand Up @@ -775,7 +787,7 @@ def create(self, validated_data: dict[str, JSON]) -> ArtifactVersionMigration:
if version.migrations.filter(
status=ArtifactVersionMigration.MigrationStatus.IN_PROGRESS
).exists():
raise MethodNotAllowed(
raise ConflictError(
f"Artifact version {artifact.uuid}/{version.slug} "
f"already has a migration in progress."
)
Expand Down
12 changes: 8 additions & 4 deletions trovi/api/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,18 @@ def migrate_artifact_version(migration: ArtifactVersionMigration):
migration_status = ArtifactVersionMigration.MigrationStatus
dest_backend_name = migration.backend
source = migration.source_urn
version = migration.artifact_version
# urn:trovi:contents:<backend>:<id>
source_backend_name, source_id = source.split(":")[3:5]
source_backend = get_backend(
source_backend_name, content_id=source_id, version=migration.artifact_version
source_backend_name, content_id=source_id, version=version
)
dest_backend = get_backend(
dest_backend_name,
version=migration.artifact_version,
version=version,
)
with ArtifactVersionMigrationErrorHandler(migration) as error_handler:
LOG.info(f"Beginning migration: {source} to {dest_backend_name}")
dest_backend.open()
# We want to throw first via the source backend
# so uploads are short-circuited
Expand Down Expand Up @@ -140,8 +142,10 @@ def migrate_artifact_version(migration: ArtifactVersionMigration):
destination_urn=dest_backend.to_urn(),
)
with transaction.atomic():
migration.artifact_version.contents_urn = dest_backend.to_urn()
migration.artifact_version.save()
version.contents_urn = dest_backend.to_urn()
version.save()

LOG.info(f"Finished migration: {source} to {version.contents_urn}")

# New threads get their own DB connection which has to be manually closed
db.connection.close()
Expand Down
7 changes: 7 additions & 0 deletions trovi/api/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
from functools import cache

from django.db import transaction
Expand Down Expand Up @@ -54,6 +55,8 @@
from trovi.models import Artifact, ArtifactVersion, ArtifactRole
from trovi.storage.serializers import StorageRequestSerializer

LOG = logging.getLogger(__name__)


@extend_schema_view(
list=extend_schema(
Expand Down Expand Up @@ -235,7 +238,11 @@ def create(self, request: Request, *args, **kwargs) -> Response:
@transaction.atomic
def unassign(self, request: Request, *args, **kwargs) -> Response:
role = self.get_object()
user = role.user
role_type = role.role
artifact = role.artifact
role.delete()
LOG.info(f"Unassigned Role: {user} from {role_type} on {artifact.uuid}")
return Response(status=status.HTTP_204_NO_CONTENT)


Expand Down
2 changes: 1 addition & 1 deletion trovi/common/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def has_permission(self, request: Request, view: views.View) -> bool:
artifact_uuid = view.kwargs.get("parent_lookup_artifact")
if not artifact_uuid:
raise ValueError(
"/versions/ endpoint was called without a parent artifact. "
"child endpoint of /artifacts was called without a parent artifact. "
"Routes are misconfigured."
)
artifact = generics.get_object_or_404(
Expand Down
4 changes: 4 additions & 0 deletions trovi/common/serializers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
from typing import Type, Any, Callable, Optional

from django.conf import settings
Expand All @@ -11,6 +12,8 @@
from trovi.fields import URNField
from util.types import JSON

LOG = logging.getLogger(__name__)


class JsonPointerField(serializers.RegexField):
regex = r"^(/[^/~]*(~[01][^/~]*)*)*$"
Expand Down Expand Up @@ -126,6 +129,7 @@ def _is_valid_force_request(self: serializers.Serializer) -> bool:
if not token:
raise PermissionDenied("Unauthenticated user attempted to use ?force flag.")
if token.is_admin():
LOG.warning(f"Recorded a forced update from {token.to_urn()}")
self.context.setdefault("force", True)
return True
else:
Expand Down
4 changes: 3 additions & 1 deletion trovi/storage/backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,9 @@ def get_links(self) -> list[dict[str, JSON]]:
links.append(git_remote.to_json())

if not links:
raise AttributeError(f"Storage backend {self.name} could not generate ")
raise AttributeError(
f"Storage backend {self.name} could not generate links"
)

return links

Expand Down
2 changes: 1 addition & 1 deletion trovi/storage/backends/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def __init__(
# this functionality of `giturlparse` is broken currently.
if protocol not in ["https", "git"]:
raise RuntimeError(
f"Can't create a git backend for remote protocl {protocol}"
f"Can't create a git backend for remote protocol {protocol}"
)
self.parsed_git_url = parse_result
except Exception:
Expand Down

0 comments on commit 5aa90a1

Please sign in to comment.