diff --git a/src/registrar/admin.py b/src/registrar/admin.py
index 6ec27a085..415289e42 100644
--- a/src/registrar/admin.py
+++ b/src/registrar/admin.py
@@ -7,6 +7,7 @@ from django.contrib.contenttypes.models import ContentType
from django.http.response import HttpResponseRedirect
from django.urls import reverse
from epplibwrapper.errors import ErrorCode, RegistryError
+from registrar.models.domain import Domain
from registrar.models.utility.admin_sort_fields import AdminSortFields
from . import models
from auditlog.models import LogEntry # type: ignore
@@ -717,51 +718,47 @@ class DomainAdmin(ListHeaderAdmin):
return super().response_change(request, obj)
def do_delete_domain(self, request, obj):
+ if not isinstance(obj, Domain):
+ # Could be problematic if the type is similar,
+ # but not the same (same field/func names) so we err out.
+ # We do not want to accidentally delete records.
+ raise ValueError("Object is not of type Domain")
try:
obj.deletedInEpp()
obj.save()
except RegistryError as err:
- if err.is_connection_error():
- self.message_user(
- request,
- "Error connecting to the registry",
- messages.ERROR,
- )
- elif err.code == ErrorCode.OBJECT_STATUS_PROHIBITS_OPERATION:
- self.message_user(
- request,
- "Error deleting this Domain: "
+ # Human-readable mappings of ErrorCodes. Can be expanded.
+ error_messages = {
+ ErrorCode.OBJECT_STATUS_PROHIBITS_OPERATION:
f"Cannot delete Domain when in status {obj.status}",
- messages.ERROR,
- )
- elif err.code == ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION:
+ ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION:
+ "This subdomain is being used as a hostname on another domain"
+ }
+
+ message = "Cannot connect to the registry"
+ if not err.is_connection_error():
+ # If nothing is found, will default to returned err
+ message = error_messages.get(err.code, err)
+ self.message_user(request, f"Error deleting this Domain: {message}", messages.ERROR)
+ except TransitionNotAllowed as err:
+ if obj.state == Domain.State.DELETED:
self.message_user(
request,
- "Error deleting this Domain: "
- f" This subdomain is being used as a hostname on another domain",
- messages.ERROR,
- )
- elif err.code:
- self.message_user(
- request,
- f"Error deleting this Domain: {err}",
- messages.ERROR,
+ f"This domain is already deleted",
+ messages.INFO,
)
else:
- # all other type error messages, display the error
- self.message_user(request, err, messages.ERROR)
- except ValueError as err:
- self.message_user(request, err, messages.ERROR)
- except TransitionNotAllowed
- self.message_user(
- request,
- f"Error deleting this Domain: {err}",
- messages.ERROR,
- )
+ self.message_user(
+ request,
+ "Error deleting this Domain: "
+ f"Can't switch from state '{obj.state}' to 'deleted'"
+ ,
+ messages.ERROR,
+ )
else:
self.message_user(
request,
- ("Domain %s Should now be deleted " ". Thanks!") % obj.name,
+ ("Domain %s has been deleted. Thanks!") % obj.name,
)
return HttpResponseRedirect(".")
diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py
index 827d26073..e264d4aa9 100644
--- a/src/registrar/models/domain.py
+++ b/src/registrar/models/domain.py
@@ -2,7 +2,7 @@ import logging
from datetime import date
from string import digits
-from django_fsm import FSMField, transition # type: ignore
+from django_fsm import FSMField, transition, TransitionNotAllowed # type: ignore
from django.db import models
@@ -802,20 +802,14 @@ class Domain(TimeStampedModel, DomainHelper):
self._remove_client_hold()
# TODO -on the client hold ticket any additional error handling here
- @transition(field="state", source=State.ON_HOLD, target=State.DELETED)
+ @transition(field="state", source=[State.ON_HOLD, State.DNS_NEEDED], target=State.DELETED)
def deletedInEpp(self):
- """domain is deleted in epp but is saved in our database.
- Returns the request_code"""
- valid_delete_states = [
- self.State.ON_HOLD,
- self.State.DNS_NEEDED
- ]
- # Check that the domain contacts a valid status
- if (self.state not in valid_delete_states):
- raise ValueError(
- f"Invalid domain state of {self.state}. Cannot delete."
- )
-
+ """Domain is deleted in epp but is saved in our database.
+ Error handling should be provided by the caller."""
+ # While we want to log errors, we want to preserve
+ # that information when this function is called.
+ # Human-readable errors are introduced at the admin.py level,
+ # as doing everything here would reduce reliablity.
try:
logger.info("deletedInEpp()-> inside _delete_domain")
self._delete_domain()
@@ -824,6 +818,11 @@ class Domain(TimeStampedModel, DomainHelper):
f"Could not delete domain. Registry returned error: {err}"
)
raise err
+ except TransitionNotAllowed as err:
+ logger.error(
+ "Could not delete domain. FSM failure: {err}"
+ )
+ raise err
except Exception as err:
logger.error(
f"Could not delete domain. An unspecified error occured: {err}"
diff --git a/src/registrar/templates/django/admin/domain_change_form.html b/src/registrar/templates/django/admin/domain_change_form.html
index 1b8b90930..ca44aa03c 100644
--- a/src/registrar/templates/django/admin/domain_change_form.html
+++ b/src/registrar/templates/django/admin/domain_change_form.html
@@ -8,9 +8,9 @@
{% block field_sets %}