diff --git a/src/api/tests/test_available.py b/src/api/tests/test_available.py index 524fd689a..9de152b06 100644 --- a/src/api/tests/test_available.py +++ b/src/api/tests/test_available.py @@ -8,6 +8,7 @@ from django.test import RequestFactory from ..views import available, check_domain_available from .common import less_console_noise from registrar.tests.common import MockEppLib +from registrar.utility.errors import GenericError, GenericErrorCodes from unittest.mock import call from epplibwrapper import ( @@ -100,16 +101,25 @@ class AvailableViewTest(MockEppLib): response = available(request, domain="igorville") self.assertTrue(json.loads(response.content)["available"]) - def test_error_handling(self): - """Calling with bad strings raises an error.""" + def test_bad_string_handling(self): + """Calling with bad strings returns unavailable.""" bad_string = "blah!;" request = self.factory.get(API_BASE_PATH + bad_string) request.user = self.user response = available(request, domain=bad_string) self.assertFalse(json.loads(response.content)["available"]) - # domain set to raise error returns false for availability - error_domain_available = available(request, "errordomain.gov") - self.assertFalse(json.loads(error_domain_available.content)["available"]) + + def test_error_handling(self): + """Error thrown while calling availabilityAPI returns error.""" + request = self.factory.get(API_BASE_PATH + "errordomain.gov") + request.user = self.user + # domain set to raise error returns false for availability and error message + error_domain_response = available(request, domain="errordomain.gov") + self.assertFalse(json.loads(error_domain_response.content)["available"]) + self.assertEqual( + GenericError.get_error_message(GenericErrorCodes.CANNOT_CONTACT_REGISTRY), + json.loads(error_domain_response.content)["message"], + ) class AvailableAPITest(MockEppLib): diff --git a/src/api/views.py b/src/api/views.py index a9f8d7692..85ae021c9 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -5,6 +5,7 @@ from django.http import JsonResponse from django.utils.safestring import mark_safe from registrar.templatetags.url_helpers import public_site_url +from registrar.utility.errors import GenericError, GenericErrorCodes import requests @@ -30,7 +31,7 @@ DOMAIN_API_MESSAGES = { ), "invalid": "Enter a domain using only letters, numbers, or hyphens (though we don't recommend using hyphens).", "success": "That domain is available!", - "error": "Error finding domain availability.", + "error": GenericError.get_error_message(GenericErrorCodes.CANNOT_CONTACT_REGISTRY), } @@ -63,17 +64,14 @@ def check_domain_available(domain): The given domain is lowercased to match against the domains list. If the given domain doesn't end with .gov, ".gov" is added when looking for - a match. + a match. If check fails, throws a RegistryError. """ Domain = apps.get_model("registrar.Domain") - try: - if domain.endswith(".gov"): - return Domain.available(domain) - else: - # domain search string doesn't end with .gov, add it on here - return Domain.available(domain + ".gov") - except Exception: - return False + if domain.endswith(".gov"): + return Domain.available(domain) + else: + # domain search string doesn't end with .gov, add it on here + return Domain.available(domain + ".gov") @require_http_methods(["GET"]) diff --git a/src/registrar/forms/application_wizard.py b/src/registrar/forms/application_wizard.py index ad11ebfd3..9a8899e2b 100644 --- a/src/registrar/forms/application_wizard.py +++ b/src/registrar/forms/application_wizard.py @@ -399,6 +399,8 @@ class AlternativeDomainForm(RegistrarForm): raise forms.ValidationError(DOMAIN_API_MESSAGES["extra_dots"], code="extra_dots") except errors.DomainUnavailableError: raise forms.ValidationError(DOMAIN_API_MESSAGES["unavailable"], code="unavailable") + except errors.RegistrySystemError: + raise forms.ValidationError(DOMAIN_API_MESSAGES["error"], code="error") except ValueError: raise forms.ValidationError(DOMAIN_API_MESSAGES["invalid"], code="invalid") return validated @@ -484,6 +486,8 @@ class DotGovDomainForm(RegistrarForm): raise forms.ValidationError(DOMAIN_API_MESSAGES["extra_dots"], code="extra_dots") except errors.DomainUnavailableError: raise forms.ValidationError(DOMAIN_API_MESSAGES["unavailable"], code="unavailable") + except errors.RegistrySystemError: + raise forms.ValidationError(DOMAIN_API_MESSAGES["error"], code="error") except ValueError: raise forms.ValidationError(DOMAIN_API_MESSAGES["invalid"], code="invalid") return validated diff --git a/src/registrar/management/commands/load_transition_domain.py b/src/registrar/management/commands/load_transition_domain.py index 6566a2f16..e1165bf9f 100644 --- a/src/registrar/management/commands/load_transition_domain.py +++ b/src/registrar/management/commands/load_transition_domain.py @@ -176,6 +176,7 @@ class Command(BaseCommand): "clienthold": TransitionDomain.StatusChoices.ON_HOLD, "created": TransitionDomain.StatusChoices.READY, "ok": TransitionDomain.StatusChoices.READY, + "unknown": TransitionDomain.StatusChoices.UNKNOWN, } mapped_status = status_maps.get(status_to_map) return mapped_status diff --git a/src/registrar/migrations/0048_alter_transitiondomain_status.py b/src/registrar/migrations/0048_alter_transitiondomain_status.py new file mode 100644 index 000000000..d67c91e4b --- /dev/null +++ b/src/registrar/migrations/0048_alter_transitiondomain_status.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.7 on 2023-12-01 17:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0047_transitiondomain_address_line_transitiondomain_city_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="transitiondomain", + name="status", + field=models.CharField( + blank=True, + choices=[("ready", "Ready"), ("on hold", "On Hold"), ("unknown", "Unknown")], + default="ready", + help_text="domain status during the transfer", + max_length=255, + verbose_name="Status", + ), + ), + ] diff --git a/src/registrar/models/transition_domain.py b/src/registrar/models/transition_domain.py index 2359a4266..28bdc4fc7 100644 --- a/src/registrar/models/transition_domain.py +++ b/src/registrar/models/transition_domain.py @@ -5,6 +5,7 @@ from .utility.time_stamped_model import TimeStampedModel class StatusChoices(models.TextChoices): READY = "ready", "Ready" ON_HOLD = "on hold", "On Hold" + UNKNOWN = "unknown", "Unknown" class TransitionDomain(TimeStampedModel): diff --git a/src/registrar/models/utility/domain_helper.py b/src/registrar/models/utility/domain_helper.py index 49badd5d7..e43661b1d 100644 --- a/src/registrar/models/utility/domain_helper.py +++ b/src/registrar/models/utility/domain_helper.py @@ -2,6 +2,7 @@ import re from api.views import check_domain_available from registrar.utility import errors +from epplibwrapper.errors import RegistryError class DomainHelper: @@ -29,19 +30,19 @@ class DomainHelper: if not isinstance(domain, str): raise ValueError("Domain name must be a string") domain = domain.lower().strip() - if domain == "": - if blank_ok: - return domain - else: - raise errors.BlankValueError() + if domain == "" and not blank_ok: + raise errors.BlankValueError() if domain.endswith(".gov"): domain = domain[:-4] if "." in domain: raise errors.ExtraDotsError() if not DomainHelper.string_could_be_domain(domain + ".gov"): raise ValueError() - if not check_domain_available(domain): - raise errors.DomainUnavailableError() + try: + if not check_domain_available(domain): + raise errors.DomainUnavailableError() + except RegistryError as err: + raise errors.RegistrySystemError() from err return domain @classmethod diff --git a/src/registrar/templates/application_form.html b/src/registrar/templates/application_form.html index 744f4e5f2..db72a1fc2 100644 --- a/src/registrar/templates/application_form.html +++ b/src/registrar/templates/application_form.html @@ -22,6 +22,14 @@ {% include "includes/form_messages.html" %} {% endblock %} +{% if pending_requests_message %} +