diff --git a/src/api/tests/test_available.py b/src/api/tests/test_available.py index 9eab17bf7..524fd689a 100644 --- a/src/api/tests/test_available.py +++ b/src/api/tests/test_available.py @@ -5,14 +5,13 @@ import json from django.contrib.auth import get_user_model from django.test import RequestFactory -from ..views import available, in_domains +from ..views import available, check_domain_available from .common import less_console_noise from registrar.tests.common import MockEppLib from unittest.mock import call from epplibwrapper import ( commands, - RegistryError, ) API_BASE_PATH = "/api/v1/available/" @@ -37,10 +36,10 @@ class AvailableViewTest(MockEppLib): response_object = json.loads(response.content) self.assertIn("available", response_object) - def test_in_domains_makes_calls_(self): + def test_domain_available_makes_calls_(self): """Domain searches successfully make correct mock EPP calls""" - gsa_available = in_domains("gsa.gov") - igorville_available = in_domains("igorvilleremixed.gov") + gsa_available = check_domain_available("gsa.gov") + igorville_available = check_domain_available("igorville.gov") """Domain searches successfully make mock EPP calls""" self.mockedSendFunction.assert_has_calls( @@ -53,29 +52,32 @@ class AvailableViewTest(MockEppLib): ), call( commands.CheckDomain( - ["igorvilleremixed.gov"], + ["igorville.gov"], ), cleaned=True, ), ] ) """Domain searches return correct availability results""" - self.assertTrue(gsa_available) - self.assertFalse(igorville_available) + self.assertFalse(gsa_available) + self.assertTrue(igorville_available) - def test_in_domains_capitalized(self): + def test_domain_available_capitalized(self): """Domain searches work without case sensitivity""" - self.assertTrue(in_domains("gsa.gov")) - # input is lowercased so GSA.GOV should be found - self.assertTrue(in_domains("GSA.gov")) + self.assertFalse(check_domain_available("gsa.gov")) + self.assertTrue(check_domain_available("igorville.gov")) + # input is lowercased so GSA.GOV should also not be available + self.assertFalse(check_domain_available("GSA.gov")) + # input is lowercased so IGORVILLE.GOV should also not be available + self.assertFalse(check_domain_available("IGORVILLE.gov")) - def test_in_domains_dotgov(self): + def test_domain_available_dotgov(self): """Domain searches work without trailing .gov""" - self.assertTrue(in_domains("gsa")) + self.assertFalse(check_domain_available("gsa")) # input is lowercased so GSA.GOV should be found - self.assertTrue(in_domains("GSA")) - # This domain should not have been registered - self.assertFalse(in_domains("igorvilleremixed")) + self.assertFalse(check_domain_available("GSA")) + # This domain should be available to register + self.assertTrue(check_domain_available("igorville")) def test_not_available_domain(self): """gsa.gov is not available""" @@ -85,17 +87,17 @@ class AvailableViewTest(MockEppLib): self.assertFalse(json.loads(response.content)["available"]) def test_available_domain(self): - """igorvilleremixed.gov is still available""" - request = self.factory.get(API_BASE_PATH + "igorvilleremixed.gov") + """igorville.gov is still available""" + request = self.factory.get(API_BASE_PATH + "igorville.gov") request.user = self.user - response = available(request, domain="igorvilleremixed.gov") + response = available(request, domain="igorville.gov") self.assertTrue(json.loads(response.content)["available"]) def test_available_domain_dotgov(self): - """igorvilleremixed.gov is still available even without the .gov suffix""" - request = self.factory.get(API_BASE_PATH + "igorvilleremixed") + """igorville.gov is still available even without the .gov suffix""" + request = self.factory.get(API_BASE_PATH + "igorville") request.user = self.user - response = available(request, domain="igorvilleremixed") + response = available(request, domain="igorville") self.assertTrue(json.loads(response.content)["available"]) def test_error_handling(self): @@ -105,10 +107,9 @@ class AvailableViewTest(MockEppLib): request.user = self.user response = available(request, domain=bad_string) self.assertFalse(json.loads(response.content)["available"]) - # domain set to raise error successfully raises error - with self.assertRaises(RegistryError): - error_domain_available = available(request, "errordomain.gov") - self.assertFalse(json.loads(error_domain_available.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"]) class AvailableAPITest(MockEppLib): diff --git a/src/api/views.py b/src/api/views.py index e8b8431de..694bea349 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -5,6 +5,8 @@ from django.http import JsonResponse import requests +from login_required import login_not_required + from cachetools.func import ttl_cache @@ -23,6 +25,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.", } @@ -50,22 +53,26 @@ def _domains(): return domains -def in_domains(domain): - """Return true if the given domain is in the domains list. +def check_domain_available(domain): + """Return true if the given domain is available. 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. """ Domain = apps.get_model("registrar.Domain") - 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") + 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 @require_http_methods(["GET"]) +@login_not_required def available(request, domain=""): """Is a given domain available or not. @@ -83,11 +90,16 @@ def available(request, domain=""): {"available": False, "message": DOMAIN_API_MESSAGES["invalid"]} ) # a domain is available if it is NOT in the list of current domains - if in_domains(domain): + try: + if check_domain_available(domain): + return JsonResponse( + {"available": True, "message": DOMAIN_API_MESSAGES["success"]} + ) + else: + return JsonResponse( + {"available": False, "message": DOMAIN_API_MESSAGES["unavailable"]} + ) + except Exception: return JsonResponse( - {"available": False, "message": DOMAIN_API_MESSAGES["unavailable"]} - ) - else: - return JsonResponse( - {"available": True, "message": DOMAIN_API_MESSAGES["success"]} + {"available": False, "message": DOMAIN_API_MESSAGES["error"]} ) diff --git a/src/registrar/migrations/0043_domain_expiration_date.py b/src/registrar/migrations/0043_domain_expiration_date.py new file mode 100644 index 000000000..f2269094c --- /dev/null +++ b/src/registrar/migrations/0043_domain_expiration_date.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.6 on 2023-10-30 15:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0042_create_groups_v03"), + ] + + operations = [ + migrations.AddField( + model_name="domain", + name="expiration_date", + field=models.DateField( + help_text="Duplication of registry's expirationdate saved for ease of reporting", + null=True, + ), + ), + ] diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index e8cc36a1c..edccbce06 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -5,6 +5,7 @@ import re from datetime import date from string import digits from typing import Optional + from django_fsm import FSMField, transition, TransitionNotAllowed # type: ignore from django.db import models @@ -29,6 +30,7 @@ from epplibwrapper import ( from registrar.models.utility.contact_error import ContactError, ContactErrorCodes +from django.db.models import DateField from .utility.domain_field import DomainField from .utility.domain_helper import DomainHelper from .utility.time_stamped_model import TimeStampedModel @@ -209,12 +211,12 @@ class Domain(TimeStampedModel, DomainHelper): return self._get_property("up_date") @Cache - def expiration_date(self) -> date: + def registry_expiration_date(self) -> date: """Get or set the `ex_date` element from the registry.""" return self._get_property("ex_date") - @expiration_date.setter # type: ignore - def expiration_date(self, ex_date: date): + @registry_expiration_date.setter # type: ignore + def registry_expiration_date(self, ex_date: date): pass @Cache @@ -958,6 +960,13 @@ class Domain(TimeStampedModel, DomainHelper): help_text="Very basic info about the lifecycle of this domain object", ) + expiration_date = DateField( + null=True, + help_text=( + "Duplication of registry's expiration" "date saved for ease of reporting" + ), + ) + def isActive(self): return self.state == Domain.State.CREATED diff --git a/src/registrar/models/utility/domain_helper.py b/src/registrar/models/utility/domain_helper.py index 8f5737915..1a77e44b1 100644 --- a/src/registrar/models/utility/domain_helper.py +++ b/src/registrar/models/utility/domain_helper.py @@ -1,6 +1,6 @@ import re -from api.views import in_domains +from api.views import check_domain_available from registrar.utility import errors @@ -44,7 +44,7 @@ class DomainHelper: raise errors.ExtraDotsError() if not DomainHelper.string_could_be_domain(domain + ".gov"): raise ValueError() - if in_domains(domain): + if not check_domain_available(domain): raise errors.DomainUnavailableError() return domain diff --git a/src/registrar/templates/domain_users.html b/src/registrar/templates/domain_users.html index f66eef5a6..7d522a515 100644 --- a/src/registrar/templates/domain_users.html +++ b/src/registrar/templates/domain_users.html @@ -12,11 +12,13 @@ email, and DNS name servers.

-