mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-31 09:43:54 +02:00
ran linter
This commit is contained in:
parent
a9f608e353
commit
6a3a5534db
5 changed files with 116 additions and 111 deletions
|
@ -146,7 +146,6 @@ class DomainAdmin(ListHeaderAdmin):
|
||||||
readonly_fields = ["state"]
|
readonly_fields = ["state"]
|
||||||
|
|
||||||
def response_change(self, request, obj):
|
def response_change(self, request, obj):
|
||||||
print(request.POST)
|
|
||||||
ACTION_BUTTON = "_place_client_hold"
|
ACTION_BUTTON = "_place_client_hold"
|
||||||
GET_SECURITY_EMAIL = "_get_security_email"
|
GET_SECURITY_EMAIL = "_get_security_email"
|
||||||
SET_SECURITY_CONTACT = "_set_security_contact"
|
SET_SECURITY_CONTACT = "_set_security_contact"
|
||||||
|
@ -157,10 +156,8 @@ class DomainAdmin(ListHeaderAdmin):
|
||||||
SET_CLIENT_HOLD = "_set_client_hold"
|
SET_CLIENT_HOLD = "_set_client_hold"
|
||||||
REMOVE_CLIENT_HOLD = "_rem_client_hold"
|
REMOVE_CLIENT_HOLD = "_rem_client_hold"
|
||||||
DELETE_DOMAIN = "_delete_domain"
|
DELETE_DOMAIN = "_delete_domain"
|
||||||
logger.info("in response")
|
|
||||||
if ACTION_BUTTON in request.POST:
|
if ACTION_BUTTON in request.POST:
|
||||||
logger.info("in action button")
|
|
||||||
print("in action button")
|
|
||||||
try:
|
try:
|
||||||
obj.place_client_hold()
|
obj.place_client_hold()
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
|
@ -181,10 +178,14 @@ class DomainAdmin(ListHeaderAdmin):
|
||||||
contacts = obj._get_property("contacts")
|
contacts = obj._get_property("contacts")
|
||||||
email = None
|
email = None
|
||||||
for contact in contacts:
|
for contact in contacts:
|
||||||
if ["type","email"] in contact.keys() and contact["type"]=="security":
|
if ["type", "email"] in contact.keys() and contact[
|
||||||
|
"type"
|
||||||
|
] == "security":
|
||||||
email = contact["email"]
|
email = contact["email"]
|
||||||
if email is None:
|
if email is None:
|
||||||
raise ValueError("Security contact type is not available on this domain")
|
raise ValueError(
|
||||||
|
"Security contact type is not available on this domain"
|
||||||
|
)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
self.message_user(request, err, messages.ERROR)
|
self.message_user(request, err, messages.ERROR)
|
||||||
else:
|
else:
|
||||||
|
@ -197,8 +198,12 @@ class DomainAdmin(ListHeaderAdmin):
|
||||||
if SET_SECURITY_CONTACT in request.POST:
|
if SET_SECURITY_CONTACT in request.POST:
|
||||||
try:
|
try:
|
||||||
fake_email = "manuallyEnteredEmail@test.gov"
|
fake_email = "manuallyEnteredEmail@test.gov"
|
||||||
if PublicContact.objects.filter(domain=obj, contact_type="security").exists():
|
if PublicContact.objects.filter(
|
||||||
sec_contact=PublicContact.objects.filter(domain=obj, contact_type="security").get()
|
domain=obj, contact_type="security"
|
||||||
|
).exists():
|
||||||
|
sec_contact = PublicContact.objects.filter(
|
||||||
|
domain=obj, contact_type="security"
|
||||||
|
).get()
|
||||||
else:
|
else:
|
||||||
sec_contact = obj.get_default_security_contact()
|
sec_contact = obj.get_default_security_contact()
|
||||||
|
|
||||||
|
@ -212,11 +217,8 @@ class DomainAdmin(ListHeaderAdmin):
|
||||||
request,
|
request,
|
||||||
("The security email is %" ". Thanks!") % fake_email,
|
("The security email is %" ". Thanks!") % fake_email,
|
||||||
)
|
)
|
||||||
print("above make domain")
|
|
||||||
|
|
||||||
if MAKE_DOMAIN in request.POST:
|
if MAKE_DOMAIN in request.POST:
|
||||||
print("in make domain")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
obj._get_or_create_domain()
|
obj._get_or_create_domain()
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
|
@ -231,8 +233,6 @@ class DomainAdmin(ListHeaderAdmin):
|
||||||
# make nameservers here
|
# make nameservers here
|
||||||
|
|
||||||
if MAKE_NAMESERVERS in request.POST:
|
if MAKE_NAMESERVERS in request.POST:
|
||||||
print("in make domain")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
hosts = [("ns1.example.com", None), ("ns2.example.com", None)]
|
hosts = [("ns1.example.com", None), ("ns2.example.com", None)]
|
||||||
obj.nameservers = hosts
|
obj.nameservers = hosts
|
||||||
|
@ -245,8 +245,6 @@ class DomainAdmin(ListHeaderAdmin):
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(".")
|
return HttpResponseRedirect(".")
|
||||||
if GET_NAMESERVERS in request.POST:
|
if GET_NAMESERVERS in request.POST:
|
||||||
print("in make domain")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
nameservers = obj.nameservers
|
nameservers = obj.nameservers
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
|
@ -259,8 +257,6 @@ class DomainAdmin(ListHeaderAdmin):
|
||||||
return HttpResponseRedirect(".")
|
return HttpResponseRedirect(".")
|
||||||
|
|
||||||
if GET_STATUS in request.POST:
|
if GET_STATUS in request.POST:
|
||||||
print("in make domain")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
statuses = obj.statuses
|
statuses = obj.statuses
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
|
@ -273,8 +269,6 @@ class DomainAdmin(ListHeaderAdmin):
|
||||||
return HttpResponseRedirect(".")
|
return HttpResponseRedirect(".")
|
||||||
|
|
||||||
if SET_CLIENT_HOLD in request.POST:
|
if SET_CLIENT_HOLD in request.POST:
|
||||||
print("in make domain")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
obj.clientHold()
|
obj.clientHold()
|
||||||
obj.save()
|
obj.save()
|
||||||
|
@ -288,8 +282,6 @@ class DomainAdmin(ListHeaderAdmin):
|
||||||
return HttpResponseRedirect(".")
|
return HttpResponseRedirect(".")
|
||||||
|
|
||||||
if REMOVE_CLIENT_HOLD in request.POST:
|
if REMOVE_CLIENT_HOLD in request.POST:
|
||||||
print("in make domain")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
obj.revertClientHold()
|
obj.revertClientHold()
|
||||||
obj.save()
|
obj.save()
|
||||||
|
@ -302,8 +294,6 @@ class DomainAdmin(ListHeaderAdmin):
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(".")
|
return HttpResponseRedirect(".")
|
||||||
if DELETE_DOMAIN in request.POST:
|
if DELETE_DOMAIN in request.POST:
|
||||||
print("in make domain")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
obj.deleted()
|
obj.deleted()
|
||||||
obj.save()
|
obj.save()
|
||||||
|
@ -318,7 +308,6 @@ class DomainAdmin(ListHeaderAdmin):
|
||||||
return super().response_change(request, obj)
|
return super().response_change(request, obj)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ContactAdmin(ListHeaderAdmin):
|
class ContactAdmin(ListHeaderAdmin):
|
||||||
"""Custom contact admin class to add search."""
|
"""Custom contact admin class to add search."""
|
||||||
|
|
||||||
|
|
|
@ -227,7 +227,7 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
hosts = self._get_property("hosts")
|
hosts = self._get_property("hosts")
|
||||||
except KeyError as err:
|
except Exception as err:
|
||||||
logger.info("Domain is missing nameservers")
|
logger.info("Domain is missing nameservers")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -260,10 +260,10 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
"""Call _check_host first before using this function,
|
"""Call _check_host first before using this function,
|
||||||
This creates the host object in the registry
|
This creates the host object in the registry
|
||||||
doesn't add the created host to the domain
|
doesn't add the created host to the domain
|
||||||
returns int response code"""
|
returns ErrorCode (int)"""
|
||||||
logger.info("_create_host()->addresses is NONE")
|
logger.info("_create_host()->addresses is NONE")
|
||||||
|
# TODO - # [epp.Ip(addr="127.0.0.1"), epp.Ip(addr="0:0:0:0:0:0:0:1", ip="v6")]
|
||||||
if not addrs is None and addrs!=[]:
|
if not addrs is None:
|
||||||
logger.info("addresses is not None %s" % addrs)
|
logger.info("addresses is not None %s" % addrs)
|
||||||
addresses = [epp.Ip(addr=addr) for addr in addrs]
|
addresses = [epp.Ip(addr=addr) for addr in addrs]
|
||||||
request = commands.CreateHost(name=host, addrs=addresses)
|
request = commands.CreateHost(name=host, addrs=addresses)
|
||||||
|
@ -271,7 +271,7 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
logger.info("_create_host()-> address IS None")
|
logger.info("_create_host()-> address IS None")
|
||||||
|
|
||||||
request = commands.CreateHost(name=host)
|
request = commands.CreateHost(name=host)
|
||||||
# [epp.Ip(addr="127.0.0.1"), epp.Ip(addr="0:0:0:0:0:0:0:1", ip="v6")]
|
|
||||||
try:
|
try:
|
||||||
logger.info("_create_host()-> sending req as %s" % request)
|
logger.info("_create_host()-> sending req as %s" % request)
|
||||||
response = registry.send(request, cleaned=True)
|
response = registry.send(request, cleaned=True)
|
||||||
|
@ -287,7 +287,7 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
example: [(ns1.okay.gov, 127.0.0.1, others ips)]"""
|
example: [(ns1.okay.gov, 127.0.0.1, others ips)]"""
|
||||||
# TODO: call EPP to set this info.
|
# TODO: call EPP to set this info.
|
||||||
# if two nameservers change state to created, don't do it automatically
|
# if two nameservers change state to created, don't do it automatically
|
||||||
hostSuccessCount = 0
|
|
||||||
if len(hosts) > 13:
|
if len(hosts) > 13:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Too many hosts provided, you may not have more than 13 nameservers."
|
"Too many hosts provided, you may not have more than 13 nameservers."
|
||||||
|
@ -303,10 +303,9 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
avail = self._check_host([host])
|
avail = self._check_host([host])
|
||||||
if avail:
|
if avail:
|
||||||
createdCode = self._create_host(host=host, addrs=addrs)
|
createdCode = self._create_host(host=host, addrs=addrs)
|
||||||
if createdCode == ErrorCode.OBJECT_EXISTS:
|
|
||||||
hostSuccessCount += 1
|
# update the domain obj
|
||||||
# update the object instead
|
if createdCode == ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY:
|
||||||
elif createdCode == ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY:
|
|
||||||
# add host to domain
|
# add host to domain
|
||||||
request = commands.UpdateDomain(
|
request = commands.UpdateDomain(
|
||||||
name=self.name, add=[epp.HostObjSet([host])]
|
name=self.name, add=[epp.HostObjSet([host])]
|
||||||
|
@ -314,16 +313,19 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
registry.send(request, cleaned=True)
|
registry.send(request, cleaned=True)
|
||||||
hostSuccessCount += 1
|
|
||||||
except RegistryError as e:
|
except RegistryError as e:
|
||||||
logger.error(
|
logger.error(
|
||||||
"Error adding nameserver, code was %s error was %s"
|
"Error adding nameserver, code was %s error was %s"
|
||||||
% (e.code, e)
|
% (e.code, e)
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.state == self.State.DNS_NEEDED and hostSuccessCount >= 2:
|
try:
|
||||||
self.created()
|
self.created()
|
||||||
self.save()
|
self.save()
|
||||||
|
except:
|
||||||
|
logger.info(
|
||||||
|
"nameserver setter checked for create state and it did not succeed"
|
||||||
|
)
|
||||||
##TODO - handle removed nameservers here will need to change the state go back to DNS_NEEDED
|
##TODO - handle removed nameservers here will need to change the state go back to DNS_NEEDED
|
||||||
|
|
||||||
@Cache
|
@Cache
|
||||||
|
@ -633,6 +635,7 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
security_contact = self.get_default_security_contact()
|
security_contact = self.get_default_security_contact()
|
||||||
security_contact.save()
|
security_contact.save()
|
||||||
logger.info("done with contacts")
|
logger.info("done with contacts")
|
||||||
|
|
||||||
@security_contact.setter # type: ignore
|
@security_contact.setter # type: ignore
|
||||||
def security_contact(self, contact: PublicContact):
|
def security_contact(self, contact: PublicContact):
|
||||||
"""makes the contact in the registry,
|
"""makes the contact in the registry,
|
||||||
|
@ -787,13 +790,14 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
logger.error(e.code)
|
logger.error(e.code)
|
||||||
raise e
|
raise e
|
||||||
def addRegistrant(self):
|
|
||||||
|
|
||||||
|
def addRegistrant(self):
|
||||||
registrant = PublicContact.get_default_registrant()
|
registrant = PublicContact.get_default_registrant()
|
||||||
registrant.domain = self
|
registrant.domain = self
|
||||||
registrant.save() ##calls the registrant_contact.setter
|
registrant.save() ##calls the registrant_contact.setter
|
||||||
logger.info("registrant is %s" % registrant)
|
logger.info("registrant is %s" % registrant)
|
||||||
return registrant.registry_id
|
return registrant.registry_id
|
||||||
|
|
||||||
@transition(field="state", source=State.UNKNOWN, target=State.DNS_NEEDED)
|
@transition(field="state", source=State.UNKNOWN, target=State.DNS_NEEDED)
|
||||||
def pendingCreate(self):
|
def pendingCreate(self):
|
||||||
logger.info("In make domain in registry ")
|
logger.info("In make domain in registry ")
|
||||||
|
@ -877,7 +881,11 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
target=State.READY,
|
target=State.READY,
|
||||||
)
|
)
|
||||||
def created(self):
|
def created(self):
|
||||||
|
nameserverList = self.nameservers
|
||||||
logger.info("created()-> inside setting create")
|
logger.info("created()-> inside setting create")
|
||||||
|
if len(nameserverList) < 2 or len(nameserverList) > 13:
|
||||||
|
raise ValueError("Not ready to become created, cannot transition yet")
|
||||||
|
logger.info("able to transition to created state")
|
||||||
|
|
||||||
# TODO - in nameservers ticket check if has everything for creation
|
# TODO - in nameservers ticket check if has everything for creation
|
||||||
# admin, tech and security
|
# admin, tech and security
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
<input type="submit" value="add nameservers" name="_make_nameservers">
|
<input type="submit" value="add nameservers" name="_make_nameservers">
|
||||||
<input type="submit" value="get nameservers" name="_get_nameservers">
|
<input type="submit" value="get nameservers" name="_get_nameservers">
|
||||||
<input type="submit" value="get status" name="_get_status">
|
<input type="submit" value="get status" name="_get_status">
|
||||||
<input type="submit" value="add nameservers" name="_set_client_hold">
|
<input type="submit" value="Actual Client Hold Set" name="_set_client_hold">
|
||||||
<input type="submit" value="get nameservers" name="_rem_client_hold">
|
<input type="submit" value="remove Client Hold" name="_rem_client_hold">
|
||||||
<input type="submit" value="get status" name="_delete_domain">
|
<input type="submit" value="EPP Delete Domain" name="_delete_domain">
|
||||||
</div>
|
</div>
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -31,7 +31,7 @@ class MockEppLib(TestCase):
|
||||||
mockDataInfoDomain = fakedEppObject(
|
mockDataInfoDomain = fakedEppObject(
|
||||||
"fakepw",
|
"fakepw",
|
||||||
cr_date=datetime.datetime(2023, 5, 25, 19, 45, 35),
|
cr_date=datetime.datetime(2023, 5, 25, 19, 45, 35),
|
||||||
contacts=["123"],
|
contacts=[common.DomainContact(contact="123", type="security")],
|
||||||
hosts=["fake.host.com"],
|
hosts=["fake.host.com"],
|
||||||
)
|
)
|
||||||
infoDomainNoContact = fakedEppObject(
|
infoDomainNoContact = fakedEppObject(
|
||||||
|
@ -73,7 +73,9 @@ class MockEppLib(TestCase):
|
||||||
self.mockedSendFunction = self.mockSendPatch.start()
|
self.mockedSendFunction = self.mockSendPatch.start()
|
||||||
self.mockedSendFunction.side_effect = self.mockSend
|
self.mockedSendFunction.side_effect = self.mockSend
|
||||||
|
|
||||||
def _convertPublicContactToEpp(self, contact: PublicContact, disclose_email=False, createContact=True):
|
def _convertPublicContactToEpp(
|
||||||
|
self, contact: PublicContact, disclose_email=False, createContact=True
|
||||||
|
):
|
||||||
DF = common.DiscloseField
|
DF = common.DiscloseField
|
||||||
fields = {DF.FAX, DF.VOICE, DF.ADDR}
|
fields = {DF.FAX, DF.VOICE, DF.ADDR}
|
||||||
|
|
||||||
|
@ -174,6 +176,7 @@ class TestDomainCache(MockEppLib):
|
||||||
# send was only called once & not on the second getter call
|
# send was only called once & not on the second getter call
|
||||||
self.mockedSendFunction.assert_called_once()
|
self.mockedSendFunction.assert_called_once()
|
||||||
|
|
||||||
|
# @skip("BROKEN by newest changes-fix in getter ticket")
|
||||||
def test_cache_nested_elements(self):
|
def test_cache_nested_elements(self):
|
||||||
"""Cache works correctly with the nested objects cache and hosts"""
|
"""Cache works correctly with the nested objects cache and hosts"""
|
||||||
domain, _ = Domain.objects.get_or_create(name="igorville.gov")
|
domain, _ = Domain.objects.get_or_create(name="igorville.gov")
|
||||||
|
@ -276,8 +279,8 @@ class TestDomainCreation(TestCase):
|
||||||
self.assertIn("ok", domain.status)
|
self.assertIn("ok", domain.status)
|
||||||
|
|
||||||
def tearDown(self) -> None:
|
def tearDown(self) -> None:
|
||||||
Domain.objects.delete()
|
Domain.objects.filter(name="igorville.gov").delete()
|
||||||
# User.objects.delete()
|
Domain.objects.all().delete()
|
||||||
|
|
||||||
|
|
||||||
class TestRegistrantContacts(MockEppLib):
|
class TestRegistrantContacts(MockEppLib):
|
||||||
|
@ -486,7 +489,9 @@ class TestRegistrantContacts(MockEppLib):
|
||||||
new_contact.email = ""
|
new_contact.email = ""
|
||||||
self.domain.security_contact = new_contact
|
self.domain.security_contact = new_contact
|
||||||
|
|
||||||
print("old contact %s email is %s" % (str(old_contact), str(old_contact.email)))
|
print(
|
||||||
|
"old contact %s email is %s" % (str(old_contact), str(old_contact.email))
|
||||||
|
)
|
||||||
print("new contact %s " % new_contact)
|
print("new contact %s " % new_contact)
|
||||||
firstCreateContactCall = self._convertPublicContactToEpp(
|
firstCreateContactCall = self._convertPublicContactToEpp(
|
||||||
old_contact, disclose_email=True
|
old_contact, disclose_email=True
|
||||||
|
@ -550,7 +555,6 @@ class TestRegistrantContacts(MockEppLib):
|
||||||
print(expected_calls)
|
print(expected_calls)
|
||||||
self.mockedSendFunction.assert_has_calls(expected_calls, any_order=True)
|
self.mockedSendFunction.assert_has_calls(expected_calls, any_order=True)
|
||||||
|
|
||||||
|
|
||||||
def test_updates_security_email(self):
|
def test_updates_security_email(self):
|
||||||
"""
|
"""
|
||||||
Scenario: Registrant replaces one valid security contact email with another
|
Scenario: Registrant replaces one valid security contact email with another
|
||||||
|
@ -566,7 +570,7 @@ class TestRegistrantContacts(MockEppLib):
|
||||||
expectedCreateCommand = self._convertPublicContactToEpp(
|
expectedCreateCommand = self._convertPublicContactToEpp(
|
||||||
security_contact, disclose_email=True
|
security_contact, disclose_email=True
|
||||||
)
|
)
|
||||||
print(expectedCreateCommand)
|
|
||||||
expectedUpdateDomain = commands.UpdateDomain(
|
expectedUpdateDomain = commands.UpdateDomain(
|
||||||
name=self.domain.name,
|
name=self.domain.name,
|
||||||
add=[
|
add=[
|
||||||
|
@ -576,10 +580,15 @@ class TestRegistrantContacts(MockEppLib):
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
security_contact.email = "changedEmail@email.com"
|
security_contact.email = "changedEmail@email.com"
|
||||||
|
print("\n\n\n***********\n\n")
|
||||||
|
security_contact.save()
|
||||||
expectedSecondCreateCommand = self._convertPublicContactToEpp(
|
expectedSecondCreateCommand = self._convertPublicContactToEpp(
|
||||||
security_contact, disclose_email=True
|
security_contact, disclose_email=True
|
||||||
)
|
)
|
||||||
updateContact=self._convertPublicContactToEpp(security_contact,disclose_email=True,createContact=False)
|
updateContact = self._convertPublicContactToEpp(
|
||||||
|
security_contact, disclose_email=True, createContact=False
|
||||||
|
)
|
||||||
|
print("SECOND EXPECTED CREATE")
|
||||||
print(expectedSecondCreateCommand)
|
print(expectedSecondCreateCommand)
|
||||||
|
|
||||||
print(self.mockedSendFunction.call_args_list)
|
print(self.mockedSendFunction.call_args_list)
|
||||||
|
@ -593,7 +602,6 @@ class TestRegistrantContacts(MockEppLib):
|
||||||
self.mockedSendFunction.assert_has_calls(expected_calls, any_order=True)
|
self.mockedSendFunction.assert_has_calls(expected_calls, any_order=True)
|
||||||
assert PublicContact.objects.filter(domain=self.domain).count() == 1
|
assert PublicContact.objects.filter(domain=self.domain).count() == 1
|
||||||
|
|
||||||
|
|
||||||
@skip("not implemented yet")
|
@skip("not implemented yet")
|
||||||
def test_update_is_unsuccessful(self):
|
def test_update_is_unsuccessful(self):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue