mirror of
https://github.com/google/nomulus.git
synced 2025-07-25 12:08:36 +02:00
Send registrars poll messages when we add/remove server-side statuses (#1417)
* Send registrars poll messages when we add/remove server-side status values
This commit is contained in:
parent
a5c646fab4
commit
ff7ac45bf4
8 changed files with 279 additions and 24 deletions
|
@ -16,6 +16,7 @@ package google.registry.flows.domain;
|
|||
|
||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
|
||||
import static com.google.common.collect.Sets.symmetricDifference;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.flows.FlowUtils.persistEntityChanges;
|
||||
|
@ -43,6 +44,9 @@ import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_UPDATE;
|
|||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.dns.DnsQueue;
|
||||
import google.registry.flows.EppException;
|
||||
|
@ -76,6 +80,7 @@ import google.registry.model.eppcommon.StatusValue;
|
|||
import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Registry;
|
||||
import java.util.Optional;
|
||||
|
@ -175,6 +180,9 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
|||
Optional<BillingEvent.OneTime> statusUpdateBillingEvent =
|
||||
createBillingEventForStatusUpdates(existingDomain, newDomain, domainHistory, now);
|
||||
statusUpdateBillingEvent.ifPresent(entitiesToSave::add);
|
||||
Optional<PollMessage.OneTime> serverStatusUpdatePollMessage =
|
||||
createPollMessageForServerStatusUpdates(existingDomain, newDomain, domainHistory, now);
|
||||
serverStatusUpdatePollMessage.ifPresent(entitiesToSave::add);
|
||||
EntityChanges entityChanges =
|
||||
flowCustomLogic.beforeSave(
|
||||
BeforeSaveParameters.newBuilder()
|
||||
|
@ -306,4 +314,50 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
|||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/** Enqueues a poll message iff a superuser is adding/removing server statuses. */
|
||||
private Optional<PollMessage.OneTime> createPollMessageForServerStatusUpdates(
|
||||
DomainBase existingDomain, DomainBase newDomain, DomainHistory historyEntry, DateTime now) {
|
||||
if (registrarId.equals(existingDomain.getPersistedCurrentSponsorRegistrarId())) {
|
||||
// Don't send a poll message when a superuser registrar is updating its own domain.
|
||||
return Optional.empty();
|
||||
}
|
||||
ImmutableSortedSet<String> addedServerStatuses =
|
||||
Sets.difference(newDomain.getStatusValues(), existingDomain.getStatusValues()).stream()
|
||||
.filter(StatusValue::isServerSettable)
|
||||
.map(StatusValue::getXmlName)
|
||||
.collect(toImmutableSortedSet(Ordering.natural()));
|
||||
ImmutableSortedSet<String> removedServerStatuses =
|
||||
Sets.difference(existingDomain.getStatusValues(), newDomain.getStatusValues()).stream()
|
||||
.filter(StatusValue::isServerSettable)
|
||||
.map(StatusValue::getXmlName)
|
||||
.collect(toImmutableSortedSet(Ordering.natural()));
|
||||
|
||||
String msg = "";
|
||||
if (addedServerStatuses.size() > 0 && removedServerStatuses.size() > 0) {
|
||||
msg =
|
||||
String.format(
|
||||
"The registry administrator has added the status(es) %s and removed the status(es)"
|
||||
+ " %s.",
|
||||
addedServerStatuses, removedServerStatuses);
|
||||
} else if (addedServerStatuses.size() > 0) {
|
||||
msg =
|
||||
String.format(
|
||||
"The registry administrator has added the status(es) %s.", addedServerStatuses);
|
||||
} else if (removedServerStatuses.size() > 0) {
|
||||
msg =
|
||||
String.format(
|
||||
"The registry administrator has removed the status(es) %s.", removedServerStatuses);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.ofNullable(
|
||||
new PollMessage.OneTime.Builder()
|
||||
.setParent(historyEntry)
|
||||
.setEventTime(now)
|
||||
.setRegistrarId(existingDomain.getCurrentSponsorRegistrarId())
|
||||
.setMsg(msg)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,7 +112,6 @@ public enum StatusValue implements EppEnum {
|
|||
*/
|
||||
PENDING_UPDATE(AllowedOn.NONE),
|
||||
|
||||
|
||||
/** A non-client-settable status that prevents deletes of EPP resources. */
|
||||
SERVER_DELETE_PROHIBITED(AllowedOn.ALL),
|
||||
|
||||
|
@ -162,6 +161,10 @@ public enum StatusValue implements EppEnum {
|
|||
return xmlName.startsWith("client");
|
||||
}
|
||||
|
||||
public boolean isServerSettable() {
|
||||
return xmlName.startsWith("server");
|
||||
}
|
||||
|
||||
public boolean isChargedStatus() {
|
||||
return xmlName.startsWith("server") && xmlName.endsWith("Prohibited");
|
||||
}
|
||||
|
|
|
@ -1449,4 +1449,39 @@ class EppLifecycleDomainTest extends EppTestCase {
|
|||
.atTime("2001-01-08T00:00:00Z")
|
||||
.hasResponse("domain_transfer_query_response_completed_fakesite.xml");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testDomainUpdateBySuperuser_sendsPollMessage() throws Exception {
|
||||
setIsSuperuser(false);
|
||||
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
|
||||
createContacts(DateTime.parse("2000-06-01T00:00:00Z"));
|
||||
|
||||
// Create domain example.tld
|
||||
assertThatCommand(
|
||||
"domain_create_no_hosts_or_dsdata.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-06-01T00:02:00Z")
|
||||
.hasResponse(
|
||||
"domain_create_response.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
"EXDATE", "2002-06-01T00:02:00Z"));
|
||||
assertThatLogoutSucceeds();
|
||||
|
||||
setIsSuperuser(true);
|
||||
assertThatLoginSucceeds("TheRegistrar", "password2");
|
||||
// Run domain update that adds server status as superuser
|
||||
assertThatCommand("domain_update_server_hold.xml")
|
||||
.atTime("2000-06-02T13:00:00Z")
|
||||
.hasResponse("generic_success_response.xml");
|
||||
assertThatLogoutSucceeds();
|
||||
setIsSuperuser(false);
|
||||
|
||||
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
|
||||
// Verify that the owning registrar now has the correct poll message
|
||||
assertThatCommand("poll.xml")
|
||||
.atTime("2000-06-02T13:00:01Z")
|
||||
.hasResponse("poll_response_server_hold.xml");
|
||||
assertThatLogoutSucceeds();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,12 +19,22 @@ import static com.google.common.collect.Sets.union;
|
|||
import static com.google.common.io.BaseEncoding.base16;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.eppcommon.StatusValue.CLIENT_DELETE_PROHIBITED;
|
||||
import static google.registry.model.eppcommon.StatusValue.CLIENT_HOLD;
|
||||
import static google.registry.model.eppcommon.StatusValue.CLIENT_RENEW_PROHIBITED;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_DELETE_PROHIBITED;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_HOLD;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_TRANSFER_PROHIBITED;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_UPDATE;
|
||||
import static google.registry.model.tld.Registry.TldState.QUIET_PERIOD;
|
||||
import static google.registry.testing.DatabaseHelper.assertBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.assertPollMessagesForResource;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType;
|
||||
import static google.registry.testing.DatabaseHelper.getPollMessages;
|
||||
import static google.registry.testing.DatabaseHelper.loadByKey;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.newDomainBase;
|
||||
|
@ -88,7 +98,7 @@ import google.registry.model.domain.DomainHistory;
|
|||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
|
@ -155,7 +165,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
|||
.build());
|
||||
persistResource(
|
||||
new DomainHistory.Builder()
|
||||
.setType(HistoryEntry.Type.DOMAIN_CREATE)
|
||||
.setType(DOMAIN_CREATE)
|
||||
.setModificationTime(clock.nowUtc())
|
||||
.setRegistrarId(domain.getCreationRegistrarId())
|
||||
.setDomain(domain)
|
||||
|
@ -179,7 +189,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
|||
.build());
|
||||
persistResource(
|
||||
new DomainHistory.Builder()
|
||||
.setType(HistoryEntry.Type.DOMAIN_CREATE)
|
||||
.setType(DOMAIN_CREATE)
|
||||
.setModificationTime(clock.nowUtc())
|
||||
.setRegistrarId(domain.getCreationRegistrarId())
|
||||
.setDomain(domain)
|
||||
|
@ -202,8 +212,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
|||
.and()
|
||||
.hasAuthInfoPwd("2BARfoo")
|
||||
.and()
|
||||
.hasOneHistoryEntryEachOfTypes(
|
||||
HistoryEntry.Type.DOMAIN_CREATE, HistoryEntry.Type.DOMAIN_UPDATE)
|
||||
.hasOneHistoryEntryEachOfTypes(DOMAIN_CREATE, DOMAIN_UPDATE)
|
||||
.and()
|
||||
.hasLastEppUpdateTime(clock.nowUtc())
|
||||
.and()
|
||||
|
@ -329,10 +338,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
|||
assertTransactionalFlow(true);
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
DomainBase domain = reloadResourceByForeignKey();
|
||||
assertAboutDomains()
|
||||
.that(domain)
|
||||
.hasOneHistoryEntryEachOfTypes(
|
||||
HistoryEntry.Type.DOMAIN_CREATE, HistoryEntry.Type.DOMAIN_UPDATE);
|
||||
assertAboutDomains().that(domain).hasOneHistoryEntryEachOfTypes(DOMAIN_CREATE, DOMAIN_UPDATE);
|
||||
assertThat(domain.getNameservers()).hasSize(13);
|
||||
// getContacts does not return contacts of type REGISTRANT, so check these separately.
|
||||
assertThat(domain.getContacts()).hasSize(3);
|
||||
|
@ -349,12 +355,9 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
|||
persistDomain();
|
||||
runFlow();
|
||||
DomainBase domain = reloadResourceByForeignKey();
|
||||
assertAboutDomains()
|
||||
.that(domain)
|
||||
.hasOneHistoryEntryEachOfTypes(
|
||||
HistoryEntry.Type.DOMAIN_CREATE, HistoryEntry.Type.DOMAIN_UPDATE);
|
||||
assertAboutDomains().that(domain).hasOneHistoryEntryEachOfTypes(DOMAIN_CREATE, DOMAIN_UPDATE);
|
||||
assertAboutHistoryEntries()
|
||||
.that(getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_UPDATE))
|
||||
.that(getOnlyHistoryEntryOfType(domain, DOMAIN_UPDATE))
|
||||
.hasMetadataReason("domain-update-test")
|
||||
.and()
|
||||
.hasMetadataRequestedByRegistrar(true);
|
||||
|
@ -482,10 +485,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
|||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
DomainBase resource = reloadResourceByForeignKey();
|
||||
assertAboutDomains()
|
||||
.that(resource)
|
||||
.hasOnlyOneHistoryEntryWhich()
|
||||
.hasType(HistoryEntry.Type.DOMAIN_UPDATE);
|
||||
assertAboutDomains().that(resource).hasOnlyOneHistoryEntryWhich().hasType(DOMAIN_UPDATE);
|
||||
assertThat(resource.getDsData())
|
||||
.isEqualTo(
|
||||
expectedDsData.stream()
|
||||
|
@ -687,9 +687,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
|||
.setBillingTime(clock.nowUtc())
|
||||
.setParent(
|
||||
getOnlyHistoryEntryOfType(
|
||||
reloadResourceByForeignKey(),
|
||||
HistoryEntry.Type.DOMAIN_UPDATE,
|
||||
DomainHistory.class))
|
||||
reloadResourceByForeignKey(), DOMAIN_UPDATE, DomainHistory.class))
|
||||
.build());
|
||||
} else {
|
||||
assertNoBillingEvents();
|
||||
|
@ -768,7 +766,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
|||
.that(reloadResourceByForeignKey())
|
||||
.hasStatusValue(StatusValue.CLIENT_UPDATE_PROHIBITED)
|
||||
.and()
|
||||
.hasStatusValue(StatusValue.SERVER_HOLD);
|
||||
.hasStatusValue(SERVER_HOLD);
|
||||
}
|
||||
|
||||
private void doSecDnsFailingTest(
|
||||
|
@ -910,12 +908,103 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
|||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_superuserStatusValueNotClientSettable() throws Exception {
|
||||
void testSuccess_superuserCanSetServerStatusValues() throws Exception {
|
||||
setEppInput("domain_update_prohibited_status.xml");
|
||||
persistReferencedEntities();
|
||||
persistDomain();
|
||||
runFlowAssertResponse(
|
||||
CommitMode.LIVE, UserPrivileges.SUPERUSER, loadFile("generic_success_response.xml"));
|
||||
// No poll message because the server status was added by the owning registrar.
|
||||
assertThat(getPollMessages()).isEmpty();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_addingServerStatusValue_sendsPollMessage() throws Exception {
|
||||
setEppInput("domain_update_prohibited_status.xml");
|
||||
persistReferencedEntities();
|
||||
persistDomain();
|
||||
persistResource(
|
||||
reloadResourceByForeignKey()
|
||||
.asBuilder()
|
||||
.setPersistedCurrentSponsorRegistrarId("NewRegistrar")
|
||||
.build());
|
||||
runFlowAssertResponse(
|
||||
CommitMode.LIVE, UserPrivileges.SUPERUSER, loadFile("generic_success_response.xml"));
|
||||
DomainBase updatedDomain = reloadResourceByForeignKey();
|
||||
assertPollMessagesForResource(
|
||||
updatedDomain,
|
||||
new PollMessage.OneTime.Builder()
|
||||
.setEventTime(clock.nowUtc())
|
||||
.setParent(getOnlyHistoryEntryOfType(updatedDomain, DOMAIN_UPDATE))
|
||||
.setRegistrarId("NewRegistrar")
|
||||
.setMsg("The registry administrator has added the status(es) [serverHold].")
|
||||
.build());
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_removingServerStatusValue_sendsPollMessage() throws Exception {
|
||||
setEppInput("domain_update_remove_server_statuses.xml");
|
||||
persistReferencedEntities();
|
||||
persistDomain();
|
||||
persistResource(
|
||||
reloadResourceByForeignKey()
|
||||
.asBuilder()
|
||||
.setPersistedCurrentSponsorRegistrarId("NewRegistrar")
|
||||
.setStatusValues(
|
||||
ImmutableSet.of(
|
||||
SERVER_DELETE_PROHIBITED,
|
||||
SERVER_TRANSFER_PROHIBITED,
|
||||
SERVER_UPDATE_PROHIBITED,
|
||||
CLIENT_DELETE_PROHIBITED,
|
||||
CLIENT_RENEW_PROHIBITED,
|
||||
CLIENT_HOLD,
|
||||
SERVER_HOLD))
|
||||
.build());
|
||||
runFlowAssertResponse(
|
||||
CommitMode.LIVE, UserPrivileges.SUPERUSER, loadFile("generic_success_response.xml"));
|
||||
DomainBase updatedDomain = reloadResourceByForeignKey();
|
||||
assertPollMessagesForResource(
|
||||
updatedDomain,
|
||||
new PollMessage.OneTime.Builder()
|
||||
.setEventTime(clock.nowUtc())
|
||||
.setParent(getOnlyHistoryEntryOfType(updatedDomain, DOMAIN_UPDATE))
|
||||
.setRegistrarId("NewRegistrar")
|
||||
.setMsg(
|
||||
"The registry administrator has removed the status(es) [serverHold,"
|
||||
+ " serverTransferProhibited, serverUpdateProhibited].")
|
||||
.build());
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_addingAndRemovingServerStatusValues_sendsPollMessage() throws Exception {
|
||||
setEppInput("domain_update_change_server_statuses.xml");
|
||||
persistReferencedEntities();
|
||||
persistDomain();
|
||||
persistResource(
|
||||
reloadResourceByForeignKey()
|
||||
.asBuilder()
|
||||
.setPersistedCurrentSponsorRegistrarId("NewRegistrar")
|
||||
.setStatusValues(
|
||||
ImmutableSet.of(
|
||||
SERVER_DELETE_PROHIBITED,
|
||||
SERVER_TRANSFER_PROHIBITED,
|
||||
CLIENT_DELETE_PROHIBITED,
|
||||
CLIENT_RENEW_PROHIBITED))
|
||||
.build());
|
||||
runFlowAssertResponse(
|
||||
CommitMode.LIVE, UserPrivileges.SUPERUSER, loadFile("generic_success_response.xml"));
|
||||
DomainBase updatedDomain = reloadResourceByForeignKey();
|
||||
assertPollMessagesForResource(
|
||||
updatedDomain,
|
||||
new PollMessage.OneTime.Builder()
|
||||
.setEventTime(clock.nowUtc())
|
||||
.setParent(getOnlyHistoryEntryOfType(updatedDomain, DOMAIN_UPDATE))
|
||||
.setRegistrarId("NewRegistrar")
|
||||
.setMsg(
|
||||
"The registry administrator has added the status(es) [serverHold,"
|
||||
+ " serverRenewProhibited] and removed the status(es)"
|
||||
+ " [serverTransferProhibited].")
|
||||
.build());
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:add>
|
||||
<domain:status s="serverHold" lang="en">
|
||||
Payment overdue.
|
||||
</domain:status>
|
||||
<domain:status s="serverRenewProhibited"/>
|
||||
<domain:status s="clientUpdateProhibited"/>
|
||||
</domain:add>
|
||||
<domain:rem>
|
||||
<domain:status s="clientRenewProhibited"/>
|
||||
<domain:status s="serverTransferProhibited"/>
|
||||
</domain:rem>
|
||||
<domain:chg />
|
||||
</domain:update>
|
||||
</update>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -0,0 +1,19 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:add />
|
||||
<domain:rem>
|
||||
<domain:status s="clientRenewProhibited"/>
|
||||
<domain:status s="serverTransferProhibited"/>
|
||||
<domain:status s="serverHold"/>
|
||||
<domain:status s="serverUpdateProhibited"/>
|
||||
</domain:rem>
|
||||
<domain:chg />
|
||||
</domain:update>
|
||||
</update>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:add>
|
||||
<domain:status s="serverHold" />
|
||||
</domain:add>
|
||||
</domain:update>
|
||||
</update>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -0,0 +1,16 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<response>
|
||||
<result code="1301">
|
||||
<msg>Command completed successfully; ack to dequeue</msg>
|
||||
</result>
|
||||
<msgQ count="1" id="1-7-TLD-14-16-2000">
|
||||
<qDate>2000-06-02T13:00:00Z</qDate>
|
||||
<msg>The registry administrator has added the status(es) [serverHold].
|
||||
</msg>
|
||||
</msgQ>
|
||||
<trID>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
<svTRID>server-trid</svTRID>
|
||||
</trID>
|
||||
</response>
|
||||
</epp>
|
Loading…
Add table
Add a link
Reference in a new issue