mirror of
https://github.com/google/nomulus
synced 2026-07-20 23:12:40 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e201450f0 | |||
| dda9a3ef7e | |||
| 8c1fb6bf00 | |||
| 4e21152f04 | |||
| 22193474d5 | |||
| efd5244ebd | |||
| 87e5d19fe5 | |||
| bbb6174c9f | |||
| 2b826651e6 | |||
| e4132db8ed | |||
| 45d90e7c68 | |||
| 028005906a | |||
| 78d78e21cb | |||
| 2f3ac2e43b | |||
| 632e3831e5 | |||
| 9ff25f9a67 | |||
| eb1a314666 | |||
| 0e182546f9 | |||
| ad06ba2e1e | |||
| c903ed4c13 |
@@ -26,6 +26,7 @@ project.convention.plugins['war'].webAppDirName =
|
||||
apply plugin: 'com.google.cloud.tools.appengine'
|
||||
|
||||
def coreResourcesDir = "${rootDir}/core/build/resources/main"
|
||||
def coreLibsDir = "${rootDir}/core/build/libs"
|
||||
|
||||
// Get the web.xml file for the service.
|
||||
war {
|
||||
@@ -38,6 +39,10 @@ war {
|
||||
from("${coreResourcesDir}/google/registry/ui/html") {
|
||||
include "*.html"
|
||||
}
|
||||
from("${coreLibsDir}") {
|
||||
include "core.jar"
|
||||
into("WEB-INF/lib")
|
||||
}
|
||||
}
|
||||
|
||||
if (project.path == ":services:default") {
|
||||
@@ -98,6 +103,7 @@ rootProject.deploy.dependsOn appengineDeployAll
|
||||
rootProject.stage.dependsOn appengineStage
|
||||
tasks['war'].dependsOn ':core:compileProdJS'
|
||||
tasks['war'].dependsOn ':core:processResources'
|
||||
tasks['war'].dependsOn ':core:jar'
|
||||
|
||||
// Impose verification for all of the deployment tasks. We haven't found a
|
||||
// better way to do this other than to apply to each of them independently.
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
@@ -140,8 +140,8 @@ public final class AsyncTaskEnqueuer {
|
||||
}
|
||||
|
||||
/** Enqueues a task to asynchronously refresh DNS for a renamed host. */
|
||||
public void enqueueAsyncDnsRefresh(HostResource host, DateTime now) {
|
||||
VKey<HostResource> hostKey = host.createVKey();
|
||||
public void enqueueAsyncDnsRefresh(Host host, DateTime now) {
|
||||
VKey<Host> hostKey = host.createVKey();
|
||||
logger.atInfo().log("Enqueuing async DNS refresh for renamed host %s.", hostKey);
|
||||
addTaskToQueueWithRetry(
|
||||
asyncDnsRefreshPullQueue,
|
||||
|
||||
@@ -31,7 +31,7 @@ import google.registry.model.EppResource;
|
||||
import google.registry.model.EppResourceUtils;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.HistoryEntryDao;
|
||||
@@ -43,8 +43,8 @@ import google.registry.util.Clock;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Hard deletes load-test ContactResources, HostResources, their subordinate history entries, and
|
||||
* the associated ForeignKey and EppResourceIndex entities.
|
||||
* Hard deletes load-test ContactResources, Hosts, their subordinate history entries, and the
|
||||
* associated ForeignKey and EppResourceIndex entities.
|
||||
*
|
||||
* <p>This only deletes contacts and hosts, NOT domains. To delete domains, use {@link
|
||||
* DeleteProberDataAction} and pass it the TLD(s) that the load test domains were created on. Note
|
||||
@@ -93,7 +93,7 @@ public class DeleteLoadTestDataAction implements Runnable {
|
||||
() -> {
|
||||
LOAD_TEST_REGISTRARS.forEach(this::deletePollMessages);
|
||||
tm().loadAllOfStream(ContactResource.class).forEach(this::deleteContact);
|
||||
tm().loadAllOfStream(HostResource.class).forEach(this::deleteHost);
|
||||
tm().loadAllOfStream(Host.class).forEach(this::deleteHost);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -123,11 +123,11 @@ public class DeleteLoadTestDataAction implements Runnable {
|
||||
deleteResource(contact);
|
||||
}
|
||||
|
||||
private void deleteHost(HostResource host) {
|
||||
private void deleteHost(Host host) {
|
||||
if (!LOAD_TEST_REGISTRARS.contains(host.getPersistedCurrentSponsorRegistrarId())) {
|
||||
return;
|
||||
}
|
||||
VKey<HostResource> hostVKey = host.createVKey();
|
||||
VKey<Host> hostVKey = host.createVKey();
|
||||
// We can remove hosts from linked domains, so we should do so then delete the hosts
|
||||
ImmutableSet<VKey<Domain>> linkedDomains =
|
||||
EppResourceUtils.getLinkedDomainKeys(hostVKey, clock.nowUtc(), null);
|
||||
@@ -135,7 +135,7 @@ public class DeleteLoadTestDataAction implements Runnable {
|
||||
.values()
|
||||
.forEach(
|
||||
domain -> {
|
||||
ImmutableSet<VKey<HostResource>> remainingHosts =
|
||||
ImmutableSet<VKey<Host>> remainingHosts =
|
||||
domain.getNsHosts().stream()
|
||||
.filter(vkey -> !vkey.equals(hostVKey))
|
||||
.collect(toImmutableSet());
|
||||
|
||||
@@ -49,8 +49,8 @@ import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.rde.RdeMode;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.Registrar.Type;
|
||||
@@ -148,7 +148,7 @@ import org.joda.time.DateTime;
|
||||
* then loaded from the remaining referenced contact histories, and marshalled into (pending
|
||||
* deposit: deposit fragment) pairs.
|
||||
*
|
||||
* <h3>{@link HostResource}</h3>
|
||||
* <h3>{@link Host}</h3>
|
||||
*
|
||||
* Similar to {@link ContactResource}, we join the most recent host history with referenced hosts to
|
||||
* find most recent referenced hosts. For external hosts we do the same treatment as we did on
|
||||
@@ -467,10 +467,9 @@ public class RdePipeline implements Serializable {
|
||||
Counter activeDomainCounter = Metrics.counter("RDE", "ActiveDomainBase");
|
||||
Counter domainFragmentCounter = Metrics.counter("RDE", "DomainFragment");
|
||||
Counter referencedContactCounter = Metrics.counter("RDE", "ReferencedContactResource");
|
||||
Counter referencedHostCounter = Metrics.counter("RDE", "ReferencedHostResource");
|
||||
Counter referencedHostCounter = Metrics.counter("RDE", "ReferencedHost");
|
||||
return domainHistories.apply(
|
||||
"Map DomainHistory to DepositFragment "
|
||||
+ "and emit referenced ContactResource and HostResource",
|
||||
"Map DomainHistory to DepositFragment " + "and emit referenced ContactResource and Host",
|
||||
ParDo.of(
|
||||
new DoFn<KV<String, Long>, KV<PendingDeposit, DepositFragment>>() {
|
||||
@ProcessElement
|
||||
@@ -565,10 +564,10 @@ public class RdePipeline implements Serializable {
|
||||
private PCollectionTuple processHostHistories(
|
||||
PCollection<KV<String, PendingDeposit>> referencedHosts,
|
||||
PCollection<KV<String, Long>> hostHistories) {
|
||||
Counter subordinateHostCounter = Metrics.counter("RDE", "SubordinateHostResource");
|
||||
Counter externalHostCounter = Metrics.counter("RDE", "ExternalHostResource");
|
||||
Counter subordinateHostCounter = Metrics.counter("RDE", "SubordinateHost");
|
||||
Counter externalHostCounter = Metrics.counter("RDE", "ExternalHost");
|
||||
Counter externalHostFragmentCounter = Metrics.counter("RDE", "ExternalHostFragment");
|
||||
return removeUnreferencedResource(referencedHosts, hostHistories, HostResource.class)
|
||||
return removeUnreferencedResource(referencedHosts, hostHistories, Host.class)
|
||||
.apply(
|
||||
"Map external DomainResource to DepositFragment and process subordinate domains",
|
||||
ParDo.of(
|
||||
@@ -576,8 +575,8 @@ public class RdePipeline implements Serializable {
|
||||
@ProcessElement
|
||||
public void processElement(
|
||||
@Element KV<String, CoGbkResult> kv, MultiOutputReceiver receiver) {
|
||||
HostResource host =
|
||||
(HostResource)
|
||||
Host host =
|
||||
(Host)
|
||||
loadResourceByHistoryEntryId(
|
||||
HostHistory.class,
|
||||
kv.getKey(),
|
||||
@@ -631,11 +630,9 @@ public class RdePipeline implements Serializable {
|
||||
Counter referencedSubordinateHostCounter = Metrics.counter("RDE", "ReferencedSubordinateHost");
|
||||
return KeyedPCollectionTuple.of(HOST_TO_PENDING_DEPOSIT, superordinateDomains)
|
||||
.and(REVISION_ID, domainHistories)
|
||||
.apply("Join Host:PendingDeposits with DomainHistory on Domain", CoGroupByKey.create())
|
||||
.apply(
|
||||
"Join HostResource:PendingDeposits with DomainHistory on DomainResource",
|
||||
CoGroupByKey.create())
|
||||
.apply(
|
||||
" Remove unreferenced DomainResource",
|
||||
" Remove unreferenced Domain",
|
||||
Filter.by(
|
||||
kv -> {
|
||||
boolean toInclude =
|
||||
@@ -647,7 +644,7 @@ public class RdePipeline implements Serializable {
|
||||
return toInclude;
|
||||
}))
|
||||
.apply(
|
||||
"Map subordinate HostResource to DepositFragment",
|
||||
"Map subordinate Host to DepositFragment",
|
||||
FlatMapElements.into(
|
||||
kvs(
|
||||
TypeDescriptor.of(PendingDeposit.class),
|
||||
@@ -664,8 +661,8 @@ public class RdePipeline implements Serializable {
|
||||
new ImmutableSet.Builder<>();
|
||||
for (KV<String, CoGbkResult> hostToPendingDeposits :
|
||||
kv.getValue().getAll(HOST_TO_PENDING_DEPOSIT)) {
|
||||
HostResource host =
|
||||
(HostResource)
|
||||
Host host =
|
||||
(Host)
|
||||
loadResourceByHistoryEntryId(
|
||||
HostHistory.class,
|
||||
hostToPendingDeposits.getKey(),
|
||||
|
||||
@@ -25,7 +25,7 @@ import google.registry.model.EppResource;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
@@ -52,7 +52,7 @@ import org.joda.time.DateTime;
|
||||
public class ResaveAllEppResourcesPipeline implements Serializable {
|
||||
|
||||
private static final ImmutableSet<Class<? extends EppResource>> EPP_RESOURCE_CLASSES =
|
||||
ImmutableSet.of(ContactResource.class, Domain.class, HostResource.class);
|
||||
ImmutableSet.of(ContactResource.class, Domain.class, Host.class);
|
||||
|
||||
/**
|
||||
* There exist three possible situations where we know we'll want to project domains to the
|
||||
|
||||
@@ -1300,6 +1300,30 @@ public final class RegistryConfig {
|
||||
return config.sslCertificateValidation.expirationWarningEmailSubjectText;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("dnsUpdateFailEmailSubjectText")
|
||||
public static String provideDnsUpdateFailEmailSubjectText(RegistryConfigSettings config) {
|
||||
return config.dnsUpdate.dnsUpdateFailEmailSubjectText;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("dnsUpdateFailEmailBodyText")
|
||||
public static String provideDnsUpdateFailEmailBodyText(RegistryConfigSettings config) {
|
||||
return config.dnsUpdate.dnsUpdateFailEmailBodyText;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("dnsUpdateFailRegistryName")
|
||||
public static String provideDnsUpdateFailRegistryName(RegistryConfigSettings config) {
|
||||
return config.dnsUpdate.dnsUpdateFailRegistryName;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("registrySupportEmail")
|
||||
public static String provideRegistrySupportEmail(RegistryConfigSettings config) {
|
||||
return config.dnsUpdate.registrySupportEmail;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("allowedEcdsaCurves")
|
||||
public static ImmutableSet<String> provideAllowedEcdsaCurves(RegistryConfigSettings config) {
|
||||
@@ -1427,6 +1451,11 @@ public final class RegistryConfig {
|
||||
return CONFIG_SETTINGS.get().caching.eppResourceMaxCachedEntries;
|
||||
}
|
||||
|
||||
/** Returns the amount of time that a particular claims list should be cached. */
|
||||
public static java.time.Duration getClaimsListCacheDuration() {
|
||||
return java.time.Duration.ofSeconds(CONFIG_SETTINGS.get().caching.claimsListCachingSeconds);
|
||||
}
|
||||
|
||||
/** Returns the email address that outgoing emails from the app are sent from. */
|
||||
public static InternetAddress getGSuiteOutgoingEmailAddress() {
|
||||
return parseEmailAddress(CONFIG_SETTINGS.get().gSuite.outgoingEmailAddress);
|
||||
|
||||
@@ -42,6 +42,7 @@ public class RegistryConfigSettings {
|
||||
public RegistryTool registryTool;
|
||||
public SslCertificateValidation sslCertificateValidation;
|
||||
public ContactHistory contactHistory;
|
||||
public DnsUpdate dnsUpdate;
|
||||
|
||||
/** Configuration options that apply to the entire App Engine project. */
|
||||
public static class AppEngine {
|
||||
@@ -155,6 +156,7 @@ public class RegistryConfigSettings {
|
||||
public boolean eppResourceCachingEnabled;
|
||||
public int eppResourceCachingSeconds;
|
||||
public int eppResourceMaxCachedEntries;
|
||||
public int claimsListCachingSeconds;
|
||||
}
|
||||
|
||||
/** Configuration for ICANN monthly reporting. */
|
||||
@@ -245,4 +247,12 @@ public class RegistryConfigSettings {
|
||||
public int minMonthsBeforeWipeOut;
|
||||
public int wipeOutQueryBatchSize;
|
||||
}
|
||||
|
||||
/** Configuration for dns update. */
|
||||
public static class DnsUpdate {
|
||||
public String dnsUpdateFailEmailSubjectText;
|
||||
public String dnsUpdateFailEmailBodyText;
|
||||
public String dnsUpdateFailRegistryName;
|
||||
public String registrySupportEmail;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,6 +294,10 @@ caching:
|
||||
# have to be very large to achieve the vast majority of possible gains.
|
||||
eppResourceMaxCachedEntries: 500
|
||||
|
||||
# Length of time that a claims list will be cached after retrieval. A fairly
|
||||
# long duration is acceptable because claims lists don't change frequently.
|
||||
claimsListCachingSeconds: 21600 # six hours
|
||||
|
||||
oAuth:
|
||||
# OAuth scopes to detect on access tokens. Superset of requiredOauthScopes.
|
||||
availableOauthScopes:
|
||||
@@ -473,6 +477,28 @@ contactHistory:
|
||||
# The batch size for querying ContactHistory table in the database.
|
||||
wipeOutQueryBatchSize: 500
|
||||
|
||||
# Configuration options relevant to the DNS update functionality.
|
||||
dnsUpdate:
|
||||
dnsUpdateFailRegistryName: Example name
|
||||
registrySupportEmail: email@example.com
|
||||
# Email subject text template to notify partners after repeatedly failing DNS update
|
||||
dnsUpdateFailEmailSubjectText: "[ACTION REQUIRED]: Incomplete DNS Update"
|
||||
# Email body text template for failing DNS update that accepts 5 parameters:
|
||||
# registrar name, domain or host address, 'domain' or 'host' as a string that failed,
|
||||
# registry support email (see dnsUpdateFailRegistrySupportEmail) and registry display name
|
||||
dnsUpdateFailEmailBodyText: >
|
||||
Dear %1$s,
|
||||
|
||||
We are contacting you regarding the changes you recently made to one of your %2$ss.
|
||||
The DNS update for the %3$s %2$s failed to process. Please review your %2$s's DNS records
|
||||
and ensure that it is valid before trying another update.
|
||||
|
||||
If you have any questions or require additional support, please contact us
|
||||
at %4$s.
|
||||
|
||||
Regards,
|
||||
%5$s
|
||||
|
||||
# Configuration options for checking SSL certificates.
|
||||
sslCertificateValidation:
|
||||
# A map specifying the maximum amount of days the certificate can be valid.
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.dns;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.dns.DnsConstants.DNS_PUBLISH_PUSH_QUEUE_NAME;
|
||||
import static google.registry.dns.DnsModule.PARAM_DNS_WRITER;
|
||||
import static google.registry.dns.DnsModule.PARAM_DOMAINS;
|
||||
@@ -22,6 +23,7 @@ import static google.registry.dns.DnsModule.PARAM_LOCK_INDEX;
|
||||
import static google.registry.dns.DnsModule.PARAM_NUM_PUBLISH_LOCKS;
|
||||
import static google.registry.dns.DnsModule.PARAM_PUBLISH_TASK_ENQUEUED;
|
||||
import static google.registry.dns.DnsModule.PARAM_REFRESH_REQUEST_CREATED;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLD;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
@@ -37,6 +39,10 @@ import google.registry.dns.DnsMetrics.ActionStatus;
|
||||
import google.registry.dns.DnsMetrics.CommitStatus;
|
||||
import google.registry.dns.DnsMetrics.PublishStatus;
|
||||
import google.registry.dns.writer.DnsWriter;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.Service;
|
||||
@@ -46,6 +52,7 @@ import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import google.registry.ui.server.SendEmailUtils;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
@@ -102,6 +109,11 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
private final Clock clock;
|
||||
private final CloudTasksUtils cloudTasksUtils;
|
||||
private final Response response;
|
||||
private final SendEmailUtils sendEmailUtils;
|
||||
private final String dnsUpdateFailEmailSubjectText;
|
||||
private final String dnsUpdateFailEmailBodyText;
|
||||
private final String dnsUpdateFailRegistryName;
|
||||
private final String registrySupportEmail;
|
||||
|
||||
@Inject
|
||||
public PublishDnsUpdatesAction(
|
||||
@@ -114,6 +126,10 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
@Parameter(PARAM_HOSTS) Set<String> hosts,
|
||||
@Parameter(PARAM_TLD) String tld,
|
||||
@Config("publishDnsUpdatesLockDuration") Duration timeout,
|
||||
@Config("dnsUpdateFailEmailSubjectText") String dnsUpdateFailEmailSubjectText,
|
||||
@Config("dnsUpdateFailEmailBodyText") String dnsUpdateFailEmailBodyText,
|
||||
@Config("dnsUpdateFailRegistryName") String dnsUpdateFailRegistryName,
|
||||
@Config("registrySupportEmail") String registrySupportEmail,
|
||||
@Header(APP_ENGINE_RETRY_HEADER) Optional<Integer> appEngineRetryCount,
|
||||
@Header(CLOUD_TASKS_RETRY_HEADER) Optional<Integer> cloudTasksRetryCount,
|
||||
DnsQueue dnsQueue,
|
||||
@@ -122,11 +138,13 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
LockHandler lockHandler,
|
||||
Clock clock,
|
||||
CloudTasksUtils cloudTasksUtils,
|
||||
SendEmailUtils sendEmailUtils,
|
||||
Response response) {
|
||||
this.dnsQueue = dnsQueue;
|
||||
this.dnsWriterProxy = dnsWriterProxy;
|
||||
this.dnsMetrics = dnsMetrics;
|
||||
this.timeout = timeout;
|
||||
this.sendEmailUtils = sendEmailUtils;
|
||||
this.retryCount =
|
||||
cloudTasksRetryCount.orElse(
|
||||
appEngineRetryCount.orElseThrow(
|
||||
@@ -143,6 +161,10 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
this.clock = clock;
|
||||
this.cloudTasksUtils = cloudTasksUtils;
|
||||
this.response = response;
|
||||
this.dnsUpdateFailEmailBodyText = dnsUpdateFailEmailBodyText;
|
||||
this.dnsUpdateFailEmailSubjectText = dnsUpdateFailEmailSubjectText;
|
||||
this.dnsUpdateFailRegistryName = dnsUpdateFailRegistryName;
|
||||
this.registrySupportEmail = registrySupportEmail;
|
||||
}
|
||||
|
||||
private void recordActionResult(ActionStatus status) {
|
||||
@@ -209,9 +231,35 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
} else if (retryCount < RETRIES_BEFORE_PERMANENT_FAILURE) {
|
||||
// If the batch only contains 1 name, allow it more retries
|
||||
throw e;
|
||||
} else {
|
||||
// By the time we get here there's either single domain or a single host
|
||||
domains.stream()
|
||||
.findFirst()
|
||||
.ifPresent(
|
||||
dn -> {
|
||||
Optional<Domain> domain = loadByForeignKey(Domain.class, dn, clock.nowUtc());
|
||||
if (domain.isPresent()) {
|
||||
notifyWithEmailAboutDnsUpdateFailure(
|
||||
domain.get().getCurrentSponsorRegistrarId(), dn, false);
|
||||
} else {
|
||||
logger.atSevere().log(String.format("Domain entity for %s not found", dn));
|
||||
}
|
||||
});
|
||||
|
||||
hosts.stream()
|
||||
.findFirst()
|
||||
.ifPresent(
|
||||
hn -> {
|
||||
Optional<Host> host = loadByForeignKey(Host.class, hn, clock.nowUtc());
|
||||
if (host.isPresent()) {
|
||||
notifyWithEmailAboutDnsUpdateFailure(
|
||||
host.get().getPersistedCurrentSponsorRegistrarId(), hn, true);
|
||||
} else {
|
||||
logger.atSevere().log(String.format("Host entity for %s not found", hn));
|
||||
}
|
||||
});
|
||||
}
|
||||
// If we get here, we should terminate this task as it is likely a perpetually failing task.
|
||||
// TODO(b/237302821): Send an email notifying partner the dns update failed
|
||||
|
||||
recordActionResult(ActionStatus.MAX_RETRIES_EXCEEDED);
|
||||
response.setStatus(SC_ACCEPTED);
|
||||
logger.atSevere().withCause(e).log("Terminated task after too many retries");
|
||||
@@ -219,6 +267,30 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Sends an email to partners regarding a failure during DNS update */
|
||||
private void notifyWithEmailAboutDnsUpdateFailure(
|
||||
String registrarId, String hostOrDomainName, Boolean isHost) {
|
||||
Optional<Registrar> registrar = Registrar.loadByRegistrarIdCached(registrarId);
|
||||
|
||||
if (registrar.isPresent()) {
|
||||
sendEmailUtils.sendEmail(
|
||||
dnsUpdateFailEmailSubjectText,
|
||||
String.format(
|
||||
dnsUpdateFailEmailBodyText,
|
||||
registrar.get().getRegistrarName(),
|
||||
hostOrDomainName,
|
||||
isHost ? "host" : "domain",
|
||||
registrySupportEmail,
|
||||
dnsUpdateFailRegistryName),
|
||||
registrar.get().getContacts().stream()
|
||||
.filter(c -> c.getTypes().contains(RegistrarPoc.Type.ADMIN))
|
||||
.map(RegistrarPoc::getEmailAddress)
|
||||
.collect(toImmutableList()));
|
||||
} else {
|
||||
logger.atSevere().log(String.format("Could not find registrar %s", registrarId));
|
||||
}
|
||||
}
|
||||
|
||||
/** Splits the domains and hosts in a batch into smaller batches and adds them to the queue. */
|
||||
private void splitBatch() {
|
||||
ImmutableList<String> domainList = ImmutableList.copyOf(domains);
|
||||
@@ -294,8 +366,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
int domainsPublished = 0;
|
||||
int domainsRejected = 0;
|
||||
for (String domain : nullToEmpty(domains)) {
|
||||
if (!DomainNameUtils.isUnder(
|
||||
InternetDomainName.from(domain), InternetDomainName.from(tld))) {
|
||||
if (!DomainNameUtils.isUnder(InternetDomainName.from(domain), InternetDomainName.from(tld))) {
|
||||
logger.atSevere().log("%s: skipping domain %s not under TLD.", tld, domain);
|
||||
domainsRejected += 1;
|
||||
} else {
|
||||
@@ -310,8 +381,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
int hostsPublished = 0;
|
||||
int hostsRejected = 0;
|
||||
for (String host : nullToEmpty(hosts)) {
|
||||
if (!DomainNameUtils.isUnder(
|
||||
InternetDomainName.from(host), InternetDomainName.from(tld))) {
|
||||
if (!DomainNameUtils.isUnder(InternetDomainName.from(host), InternetDomainName.from(tld))) {
|
||||
logger.atSevere().log("%s: skipping host %s not under TLD.", tld, host);
|
||||
hostsRejected += 1;
|
||||
} else {
|
||||
|
||||
@@ -21,7 +21,7 @@ import google.registry.model.EppResource;
|
||||
import google.registry.model.EppResource.ForeignKeyedEppResource;
|
||||
import google.registry.model.annotations.ExternalMessagingName;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.request.HttpException.NotFoundException;
|
||||
@@ -66,7 +66,7 @@ public final class RefreshDnsAction implements Runnable {
|
||||
dnsQueue.addDomainRefreshTask(domainOrHostName);
|
||||
break;
|
||||
case HOST:
|
||||
verifyHostIsSubordinate(loadAndVerifyExistence(HostResource.class, domainOrHostName));
|
||||
verifyHostIsSubordinate(loadAndVerifyExistence(Host.class, domainOrHostName));
|
||||
dnsQueue.addHostRefreshTask(domainOrHostName);
|
||||
break;
|
||||
default:
|
||||
@@ -86,7 +86,7 @@ public final class RefreshDnsAction implements Runnable {
|
||||
domainOrHostName)));
|
||||
}
|
||||
|
||||
private static void verifyHostIsSubordinate(HostResource host) {
|
||||
private static void verifyHostIsSubordinate(Host host) {
|
||||
if (!host.isSubordinate()) {
|
||||
throw new BadRequestException(
|
||||
String.format("%s isn't a subordinate hostname", host.getHostName()));
|
||||
|
||||
@@ -38,7 +38,7 @@ import google.registry.dns.writer.DnsWriter;
|
||||
import google.registry.dns.writer.DnsWriterZone;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.Concurrent;
|
||||
@@ -190,7 +190,7 @@ public class CloudDnsWriter extends BaseDnsWriter {
|
||||
// Load the target host. Note that it can be absent if this host was just deleted.
|
||||
// desiredRecords is populated with an empty set to indicate that all existing records
|
||||
// should be deleted.
|
||||
Optional<HostResource> host = loadByForeignKey(HostResource.class, hostName, clock.nowUtc());
|
||||
Optional<Host> host = loadByForeignKey(Host.class, hostName, clock.nowUtc());
|
||||
|
||||
// Return early if the host is deleted.
|
||||
if (!host.isPresent()) {
|
||||
|
||||
@@ -29,7 +29,7 @@ import google.registry.dns.writer.BaseDnsWriter;
|
||||
import google.registry.dns.writer.DnsWriterZone;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.util.Clock;
|
||||
import java.io.IOException;
|
||||
@@ -215,7 +215,7 @@ public class DnsUpdateWriter extends BaseDnsWriter {
|
||||
private void addInBailiwickNameServerSet(Domain domain, Update update) {
|
||||
for (String hostName :
|
||||
intersection(domain.loadNameserverHostNames(), domain.getSubordinateHosts())) {
|
||||
Optional<HostResource> host = loadByForeignKey(HostResource.class, hostName, clock.nowUtc());
|
||||
Optional<Host> host = loadByForeignKey(Host.class, hostName, clock.nowUtc());
|
||||
checkState(host.isPresent(), "Host %s cannot be loaded", hostName);
|
||||
update.add(makeAddressSet(host.get()));
|
||||
update.add(makeV6AddressSet(host.get()));
|
||||
@@ -236,7 +236,7 @@ public class DnsUpdateWriter extends BaseDnsWriter {
|
||||
return nameServerSet;
|
||||
}
|
||||
|
||||
private RRset makeAddressSet(HostResource host) {
|
||||
private RRset makeAddressSet(Host host) {
|
||||
RRset addressSet = new RRset();
|
||||
for (InetAddress address : host.getInetAddresses()) {
|
||||
if (address instanceof Inet4Address) {
|
||||
@@ -252,7 +252,7 @@ public class DnsUpdateWriter extends BaseDnsWriter {
|
||||
return addressSet;
|
||||
}
|
||||
|
||||
private RRset makeV6AddressSet(HostResource host) {
|
||||
private RRset makeV6AddressSet(Host host) {
|
||||
RRset addressSet = new RRset();
|
||||
for (InetAddress address : host.getInetAddresses()) {
|
||||
if (address instanceof Inet6Address) {
|
||||
|
||||
+3
-3
@@ -26,7 +26,7 @@
|
||||
<property name="creationTime" direction="desc"/>
|
||||
</datastore-index>
|
||||
<!-- For finding host resources by registrar. -->
|
||||
<datastore-index kind="HostResource" ancestor="false" source="manual">
|
||||
<datastore-index kind="Host" ancestor="false" source="manual">
|
||||
<property name="currentSponsorClientId" direction="asc"/>
|
||||
<property name="deletionTime" direction="asc"/>
|
||||
<property name="fullyQualifiedHostName" direction="asc"/>
|
||||
@@ -52,7 +52,7 @@
|
||||
<property name="deletionTime" direction="asc"/>
|
||||
</datastore-index>
|
||||
<!-- For WHOIS IP address lookup -->
|
||||
<datastore-index kind="HostResource" ancestor="false" source="manual">
|
||||
<datastore-index kind="Host" ancestor="false" source="manual">
|
||||
<property name="inetAddresses" direction="asc"/>
|
||||
<property name="deletionTime" direction="asc"/>
|
||||
</datastore-index>
|
||||
@@ -87,7 +87,7 @@
|
||||
<property name="tld" direction="asc"/>
|
||||
<property name="fullyQualifiedDomainName" direction="asc"/>
|
||||
</datastore-index>
|
||||
<datastore-index kind="HostResource" ancestor="false" source="manual">
|
||||
<datastore-index kind="Host" ancestor="false" source="manual">
|
||||
<property name="deletionTime" direction="asc"/>
|
||||
<property name="fullyQualifiedHostName" direction="asc"/>
|
||||
</datastore-index>
|
||||
|
||||
@@ -114,7 +114,7 @@ public class TlsCredentials implements TransportCredentials {
|
||||
"Authentication error: IP address %s is not allow-listed for registrar %s; allow list is:"
|
||||
+ " %s",
|
||||
clientInetAddr, registrar.getRegistrarId(), ipAddressAllowList);
|
||||
throw new BadRegistrarIpAddressException();
|
||||
throw new BadRegistrarIpAddressException(clientInetAddr);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -216,8 +216,13 @@ public class TlsCredentials implements TransportCredentials {
|
||||
|
||||
/** Registrar IP address is not in stored allow list. */
|
||||
public static class BadRegistrarIpAddressException extends AuthenticationErrorException {
|
||||
BadRegistrarIpAddressException() {
|
||||
super("Registrar IP address is not in stored allow list");
|
||||
BadRegistrarIpAddressException(Optional<InetAddress> clientInetAddr) {
|
||||
super(
|
||||
clientInetAddr.isPresent()
|
||||
? String.format(
|
||||
"Registrar IP address %s is not in stored allow list",
|
||||
clientInetAddr.get().getHostAddress())
|
||||
: "Registrar IP address is not in stored allow list");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -153,6 +153,7 @@ import org.joda.time.Duration;
|
||||
* @error {@link DomainCreateFlow.AnchorTenantCreatePeriodException}
|
||||
* @error {@link DomainCreateFlow.MustHaveSignedMarksInCurrentPhaseException}
|
||||
* @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException}
|
||||
* @error {@link DomainCreateFlow.NoTrademarkedRegistrationsBeforeSunriseException}
|
||||
* @error {@link DomainCreateFlow.SignedMarksOnlyDuringSunriseException}
|
||||
* @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException}
|
||||
* @error {@link DomainFlowTmchUtils.FoundMarkNotYetValidException}
|
||||
@@ -376,8 +377,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
.setIdnTableName(validateDomainNameWithIdnTables(domainName))
|
||||
.setRegistrationExpirationTime(registrationExpirationTime)
|
||||
.setAutorenewBillingEvent(autorenewBillingEvent.createVKey())
|
||||
.setAutorenewPollMessage(
|
||||
autorenewPollMessage.createVKey(), autorenewPollMessage.getHistoryRevisionId())
|
||||
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
|
||||
.setLaunchNotice(hasClaimsNotice ? launchCreate.get().getNotice() : null)
|
||||
.setSmdId(signedMarkId)
|
||||
.setDsData(secDnsCreate.map(SecDnsCreateExtension::getDsData).orElse(null))
|
||||
@@ -481,7 +481,8 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
boolean isValidReservedCreate,
|
||||
boolean hasSignedMarks)
|
||||
throws NoGeneralRegistrationsInCurrentPhaseException,
|
||||
MustHaveSignedMarksInCurrentPhaseException {
|
||||
MustHaveSignedMarksInCurrentPhaseException,
|
||||
NoTrademarkedRegistrationsBeforeSunriseException {
|
||||
// We allow general registration during GA.
|
||||
TldState currentState = registry.getTldState(now);
|
||||
if (currentState.equals(GENERAL_AVAILABILITY)) {
|
||||
@@ -496,16 +497,23 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
// Bypass most TLD state checks if that behavior is specified by the token
|
||||
if (behavior.equals(RegistrationBehavior.BYPASS_TLD_STATE)
|
||||
|| behavior.equals(RegistrationBehavior.ANCHOR_TENANT)) {
|
||||
// If bypassing TLD state checks, a post-sunrise state is always fine
|
||||
if (!currentState.equals(START_DATE_SUNRISE)
|
||||
&& registry.getTldStateTransitions().headMap(now).containsValue(START_DATE_SUNRISE)) {
|
||||
return;
|
||||
}
|
||||
// Non-trademarked names with the state check bypassed are always available
|
||||
if (!claimsList.getClaimKey(domainLabel).isPresent()) {
|
||||
return;
|
||||
}
|
||||
if (!currentState.equals(START_DATE_SUNRISE)) {
|
||||
// Trademarked domains cannot be registered until after the sunrise period has ended, unless
|
||||
// a valid signed mark is provided. Signed marks can only be provided during sunrise.
|
||||
// Thus, when bypassing TLD state checks, a post-sunrise state is always fine.
|
||||
if (registry.getTldStateTransitions().headMap(now).containsValue(START_DATE_SUNRISE)) {
|
||||
return;
|
||||
} else {
|
||||
// If sunrise hasn't happened yet, trademarked domains are unavailable
|
||||
throw new NoTrademarkedRegistrationsBeforeSunriseException(domainLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, signed marks are necessary and sufficient in the sunrise period
|
||||
if (currentState.equals(START_DATE_SUNRISE)) {
|
||||
if (!hasSignedMarks) {
|
||||
@@ -724,6 +732,17 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
}
|
||||
}
|
||||
|
||||
/** Trademarked domains cannot be registered before the sunrise period. */
|
||||
static class NoTrademarkedRegistrationsBeforeSunriseException
|
||||
extends ParameterValuePolicyErrorException {
|
||||
public NoTrademarkedRegistrationsBeforeSunriseException(String domainLabel) {
|
||||
super(
|
||||
String.format(
|
||||
"The trademarked label %s cannot be registered before the sunrise period.",
|
||||
domainLabel));
|
||||
}
|
||||
}
|
||||
|
||||
/** Anchor tenant domain create is for the wrong number of years. */
|
||||
static class AnchorTenantCreatePeriodException extends ParameterValuePolicyErrorException {
|
||||
public AnchorTenantCreatePeriodException(int invalidYears) {
|
||||
|
||||
@@ -64,6 +64,7 @@ import google.registry.flows.custom.DomainDeleteFlowCustomLogic.BeforeSaveParame
|
||||
import google.registry.flows.custom.EntityChanges;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||
@@ -260,8 +261,10 @@ public final class DomainDeleteFlow implements TransactionalFlow {
|
||||
handlePendingTransferOnDelete(existingDomain, newDomain, now, domainHistory);
|
||||
// Close the autorenew billing event and poll message. This may delete the poll message. Store
|
||||
// the updated recurring billing event, we'll need it later and can't reload it.
|
||||
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
|
||||
BillingEvent.Recurring recurringBillingEvent =
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, now);
|
||||
updateAutorenewRecurrenceEndTime(
|
||||
existingDomain, existingRecurring, now, domainHistory.getDomainHistoryId());
|
||||
// If there's a pending transfer, the gaining client's autorenew billing
|
||||
// event and poll message will already have been deleted in
|
||||
// ResourceDeleteFlow since it's listed in serverApproveEntities.
|
||||
|
||||
@@ -112,9 +112,10 @@ import google.registry.model.domain.secdns.SecDnsUpdateExtension;
|
||||
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Add;
|
||||
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Remove;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.Registrar.State;
|
||||
@@ -270,7 +271,13 @@ public class DomainFlowUtils {
|
||||
&& token.get().getDomainName().get().equals(domainName.toString())) {
|
||||
return true;
|
||||
}
|
||||
// Otherwise check whether the metadata extension is being used by a superuser to specify that
|
||||
// Otherwise, check to see if we're using the specialized anchor tenant registration behavior on
|
||||
// the allocation token
|
||||
if (token.isPresent()
|
||||
&& token.get().getRegistrationBehavior().equals(RegistrationBehavior.ANCHOR_TENANT)) {
|
||||
return true;
|
||||
}
|
||||
// Otherwise, check whether the metadata extension is being used by a superuser to specify that
|
||||
// it's an anchor tenant creation.
|
||||
return metadataExtension.isPresent() && metadataExtension.get().getIsAnchorTenant();
|
||||
}
|
||||
@@ -375,7 +382,7 @@ public class DomainFlowUtils {
|
||||
static void verifyNotInPendingDelete(
|
||||
Set<DesignatedContact> contacts,
|
||||
VKey<ContactResource> registrant,
|
||||
Set<VKey<HostResource>> nameservers)
|
||||
Set<VKey<Host>> nameservers)
|
||||
throws EppException {
|
||||
ImmutableList.Builder<VKey<? extends EppResource>> keysToLoad = new ImmutableList.Builder<>();
|
||||
contacts.stream().map(DesignatedContact::getContactKey).forEach(keysToLoad::add);
|
||||
@@ -583,7 +590,11 @@ public class DomainFlowUtils {
|
||||
*
|
||||
* <p>Returns the new autorenew recurring billing event.
|
||||
*/
|
||||
public static Recurring updateAutorenewRecurrenceEndTime(Domain domain, DateTime newEndTime) {
|
||||
public static Recurring updateAutorenewRecurrenceEndTime(
|
||||
Domain domain,
|
||||
Recurring existingRecurring,
|
||||
DateTime newEndTime,
|
||||
@Nullable DomainHistoryId historyId) {
|
||||
Optional<PollMessage.Autorenew> autorenewPollMessage =
|
||||
tm().loadByKeyIfPresent(domain.getAutorenewPollMessage());
|
||||
|
||||
@@ -591,33 +602,34 @@ public class DomainFlowUtils {
|
||||
// create a new one at the same id. This can happen if a transfer was requested on a domain
|
||||
// where all autorenew poll messages had already been delivered (this would cause the poll
|
||||
// message to be deleted), and then subsequently the transfer was canceled, rejected, or deleted
|
||||
// (which would cause the poll message to be recreated here).
|
||||
PollMessage.Autorenew updatedAutorenewPollMessage =
|
||||
autorenewPollMessage.isPresent()
|
||||
? autorenewPollMessage.get().asBuilder().setAutorenewEndTime(newEndTime).build()
|
||||
: newAutorenewPollMessage(domain)
|
||||
.setId((Long) domain.getAutorenewPollMessage().getSqlKey())
|
||||
.setAutorenewEndTime(newEndTime)
|
||||
.setDomainHistoryId(
|
||||
new DomainHistoryId(
|
||||
domain.getRepoId(), domain.getAutorenewPollMessageHistoryId()))
|
||||
.build();
|
||||
// (which would cause the poll message to be recreated here). In the latter case, the history id
|
||||
// of the event that created the new poll message will also be used.
|
||||
PollMessage.Autorenew updatedAutorenewPollMessage;
|
||||
if (autorenewPollMessage.isPresent()) {
|
||||
updatedAutorenewPollMessage =
|
||||
autorenewPollMessage.get().asBuilder().setAutorenewEndTime(newEndTime).build();
|
||||
} else {
|
||||
checkNotNull(
|
||||
historyId, "Cannot create a new autorenew poll message without a domain history id");
|
||||
updatedAutorenewPollMessage =
|
||||
newAutorenewPollMessage(domain)
|
||||
.setId((Long) domain.getAutorenewPollMessage().getSqlKey())
|
||||
.setAutorenewEndTime(newEndTime)
|
||||
.setDomainHistoryId(historyId)
|
||||
.build();
|
||||
}
|
||||
|
||||
// If the resultant autorenew poll message would have no poll messages to deliver, then just
|
||||
// delete it. Otherwise save it with the new end time.
|
||||
// delete it. Otherwise, save it with the new end time.
|
||||
if (isAtOrAfter(updatedAutorenewPollMessage.getEventTime(), newEndTime)) {
|
||||
autorenewPollMessage.ifPresent(autorenew -> tm().delete(autorenew));
|
||||
} else {
|
||||
tm().put(updatedAutorenewPollMessage);
|
||||
}
|
||||
|
||||
Recurring recurring =
|
||||
tm().loadByKey(domain.getAutorenewBillingEvent())
|
||||
.asBuilder()
|
||||
.setRecurrenceEndTime(newEndTime)
|
||||
.build();
|
||||
tm().put(recurring);
|
||||
return recurring;
|
||||
Recurring newRecurring = existingRecurring.asBuilder().setRecurrenceEndTime(newEndTime).build();
|
||||
tm().put(newRecurring);
|
||||
return newRecurring;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -708,7 +720,10 @@ public class DomainFlowUtils {
|
||||
throw new TransfersAreAlwaysForOneYearException();
|
||||
}
|
||||
builder.setAvailIfSupported(true);
|
||||
fees = pricingLogic.getTransferPrice(registry, domainNameString, now).getFees();
|
||||
fees =
|
||||
pricingLogic
|
||||
.getTransferPrice(registry, domainNameString, now, recurringBillingEvent)
|
||||
.getFees();
|
||||
break;
|
||||
case UPDATE:
|
||||
builder.setAvailIfSupported(true);
|
||||
@@ -767,7 +782,7 @@ public class DomainFlowUtils {
|
||||
final Optional<? extends FeeTransformCommandExtension> feeCommand,
|
||||
FeesAndCredits feesAndCredits)
|
||||
throws EppException {
|
||||
if (isDomainPremium(domainName, priceTime) && !feeCommand.isPresent()) {
|
||||
if (feesAndCredits.hasAnyPremiumFees() && !feeCommand.isPresent()) {
|
||||
throw new FeesRequiredForPremiumNameException();
|
||||
}
|
||||
validateFeesAckedIfPresent(feeCommand, feesAndCredits);
|
||||
|
||||
@@ -195,9 +195,14 @@ public final class DomainPricingLogic {
|
||||
}
|
||||
|
||||
/** Returns a new transfer price for the pricer. */
|
||||
FeesAndCredits getTransferPrice(Registry registry, String domainName, DateTime dateTime)
|
||||
FeesAndCredits getTransferPrice(
|
||||
Registry registry,
|
||||
String domainName,
|
||||
DateTime dateTime,
|
||||
@Nullable Recurring recurringBillingEvent)
|
||||
throws EppException {
|
||||
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
|
||||
FeesAndCredits renewPrice =
|
||||
getRenewPrice(registry, domainName, dateTime, 1, recurringBillingEvent);
|
||||
return customLogic.customizeTransferPrice(
|
||||
TransferPriceParameters.newBuilder()
|
||||
.setFeesAndCredits(
|
||||
@@ -205,9 +210,9 @@ public final class DomainPricingLogic {
|
||||
.setCurrency(registry.getCurrency())
|
||||
.addFeeOrCredit(
|
||||
Fee.create(
|
||||
domainPrices.getRenewCost().getAmount(),
|
||||
renewPrice.getRenewCost().getAmount(),
|
||||
FeeType.RENEW,
|
||||
domainPrices.isPremium()))
|
||||
renewPrice.hasAnyPremiumFees()))
|
||||
.build())
|
||||
.setRegistry(registry)
|
||||
.setDomainName(InternetDomainName.from(domainName))
|
||||
|
||||
@@ -222,7 +222,12 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||
domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
|
||||
.build();
|
||||
// End the old autorenew billing event and poll message now. This may delete the poll message.
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, now);
|
||||
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
|
||||
updateAutorenewRecurrenceEndTime(
|
||||
existingDomain,
|
||||
existingRecurring,
|
||||
now,
|
||||
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()));
|
||||
Domain newDomain =
|
||||
existingDomain
|
||||
.asBuilder()
|
||||
@@ -230,9 +235,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||
.setLastEppUpdateRegistrarId(registrarId)
|
||||
.setRegistrationExpirationTime(newExpirationTime)
|
||||
.setAutorenewBillingEvent(newAutorenewEvent.createVKey())
|
||||
.setAutorenewPollMessage(
|
||||
newAutorenewPollMessage.createVKey(),
|
||||
newAutorenewPollMessage.getHistoryRevisionId())
|
||||
.setAutorenewPollMessage(newAutorenewPollMessage.createVKey())
|
||||
.addGracePeriod(
|
||||
GracePeriod.forBillingEvent(
|
||||
GracePeriodStatus.RENEW, existingDomain.getRepoId(), explicitRenewEvent))
|
||||
|
||||
@@ -251,8 +251,7 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow {
|
||||
.setGracePeriods(null)
|
||||
.setDeletePollMessage(null)
|
||||
.setAutorenewBillingEvent(autorenewEvent.createVKey())
|
||||
.setAutorenewPollMessage(
|
||||
autorenewPollMessage.createVKey(), autorenewPollMessage.getHistoryRevisionId())
|
||||
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
|
||||
// Clear the autorenew end time so if it had expired but is now explicitly being restored,
|
||||
// it won't immediately be deleted again.
|
||||
.setAutorenewEndTime(Optional.empty())
|
||||
|
||||
@@ -31,7 +31,6 @@ import static google.registry.model.ResourceTransferUtils.approvePendingTransfer
|
||||
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.TRANSFER_SUCCESSFUL;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
|
||||
import static google.registry.util.CollectionUtils.union;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
|
||||
@@ -45,17 +44,21 @@ import google.registry.flows.FlowModule.Superuser;
|
||||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.TransactionalFlow;
|
||||
import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.token.AllocationTokenExtension;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
@@ -85,6 +88,18 @@ import org.joda.time.DateTime;
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||
* @error {@link google.registry.flows.exceptions.NotPendingTransferException}
|
||||
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException}
|
||||
*/
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_TRANSFER_APPROVE)
|
||||
public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
@@ -96,6 +111,10 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject DomainHistory.Builder historyBuilder;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@Inject AllocationTokenFlowUtils allocationTokenFlowUtils;
|
||||
@Inject EppInput eppInput;
|
||||
|
||||
@Inject DomainTransferApproveFlow() {}
|
||||
|
||||
/**
|
||||
@@ -104,11 +123,17 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
*/
|
||||
@Override
|
||||
public EppResponse run() throws EppException {
|
||||
extensionManager.register(MetadataExtension.class);
|
||||
extensionManager.register(MetadataExtension.class, AllocationTokenExtension.class);
|
||||
validateRegistrarIsLoggedIn(registrarId);
|
||||
extensionManager.validate();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
|
||||
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
|
||||
existingDomain,
|
||||
Registry.get(existingDomain.getTld()),
|
||||
registrarId,
|
||||
now,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
verifyOptionalAuthInfo(authInfo, existingDomain);
|
||||
verifyHasPendingTransfer(existingDomain);
|
||||
verifyResourceOwnership(registrarId, existingDomain);
|
||||
@@ -120,6 +145,7 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
String gainingRegistrarId = transferData.getGainingRegistrarId();
|
||||
// Create a transfer billing event for 1 year, unless the superuser extension was used to set
|
||||
// the transfer period to zero. There is not a transfer cost if the transfer period is zero.
|
||||
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
|
||||
Key<DomainHistory> domainHistoryKey = createHistoryKey(existingDomain, DomainHistory.class);
|
||||
historyBuilder.setId(domainHistoryKey.getId());
|
||||
Optional<BillingEvent.OneTime> billingEvent =
|
||||
@@ -131,7 +157,14 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
.setTargetId(targetId)
|
||||
.setRegistrarId(gainingRegistrarId)
|
||||
.setPeriodYears(1)
|
||||
.setCost(getDomainRenewCost(targetId, transferData.getTransferRequestTime(), 1))
|
||||
.setCost(
|
||||
pricingLogic
|
||||
.getTransferPrice(
|
||||
Registry.get(tld),
|
||||
targetId,
|
||||
transferData.getTransferRequestTime(),
|
||||
existingRecurring)
|
||||
.getRenewCost())
|
||||
.setEventTime(now)
|
||||
.setBillingTime(now.plus(Registry.get(tld).getTransferGracePeriodLength()))
|
||||
.setDomainHistoryId(
|
||||
@@ -145,9 +178,9 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
getOnlyElement(existingDomain.getGracePeriodsOfType(GracePeriodStatus.AUTO_RENEW), null);
|
||||
if (autorenewGrace != null) {
|
||||
// During a normal transfer, if the domain is in the auto-renew grace period, the auto-renew
|
||||
// billing event is cancelled and the gaining registrar is charged for the one year renewal.
|
||||
// billing event is cancelled and the gaining registrar is charged for the one-year renewal.
|
||||
// But, if the superuser extension is used to request a transfer without an additional year
|
||||
// then the gaining registrar is not charged for the one year renewal and the losing registrar
|
||||
// then the gaining registrar is not charged for the one-year renewal and the losing registrar
|
||||
// still needs to be charged for the auto-renew.
|
||||
if (billingEvent.isPresent()) {
|
||||
entitiesToSave.add(
|
||||
@@ -161,7 +194,11 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
}
|
||||
// Close the old autorenew event and poll message at the transfer time (aka now). This may end
|
||||
// up deleting the poll message.
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, now);
|
||||
updateAutorenewRecurrenceEndTime(
|
||||
existingDomain,
|
||||
existingRecurring,
|
||||
now,
|
||||
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()));
|
||||
DateTime newExpirationTime =
|
||||
computeExDateForApprovalTime(existingDomain, now, transferData.getTransferPeriod());
|
||||
// Create a new autorenew event starting at the expiration time.
|
||||
@@ -172,6 +209,8 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
.setTargetId(targetId)
|
||||
.setRegistrarId(gainingRegistrarId)
|
||||
.setEventTime(newExpirationTime)
|
||||
.setRenewalPriceBehavior(existingRecurring.getRenewalPriceBehavior())
|
||||
.setRenewalPrice(existingRecurring.getRenewalPrice().orElse(null))
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.setDomainHistoryId(
|
||||
new DomainHistoryId(
|
||||
@@ -205,9 +244,7 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
.build())
|
||||
.setRegistrationExpirationTime(newExpirationTime)
|
||||
.setAutorenewBillingEvent(autorenewEvent.createVKey())
|
||||
.setAutorenewPollMessage(
|
||||
gainingClientAutorenewPollMessage.createVKey(),
|
||||
gainingClientAutorenewPollMessage.getHistoryRevisionId())
|
||||
.setAutorenewPollMessage(gainingClientAutorenewPollMessage.createVKey())
|
||||
// Remove all the old grace periods and add a new one for the transfer.
|
||||
.setGracePeriods(
|
||||
billingEvent
|
||||
|
||||
@@ -40,6 +40,7 @@ import google.registry.flows.FlowModule.Superuser;
|
||||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.TransactionalFlow;
|
||||
import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
@@ -114,7 +115,9 @@ public final class DomainTransferCancelFlow implements TransactionalFlow {
|
||||
targetId, newDomain.getTransferData(), null, domainHistoryKey));
|
||||
// Reopen the autorenew event and poll message that we closed for the implicit transfer. This
|
||||
// may recreate the autorenew poll message if it was deleted when the transfer request was made.
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, END_OF_TIME);
|
||||
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
|
||||
updateAutorenewRecurrenceEndTime(
|
||||
existingDomain, existingRecurring, END_OF_TIME, domainHistory.getDomainHistoryId());
|
||||
// Delete the billing event and poll messages that were written in case the transfer would have
|
||||
// been implicitly server approved.
|
||||
tm().delete(existingDomain.getTransferData().getServerApproveEntities());
|
||||
|
||||
@@ -42,6 +42,7 @@ import google.registry.flows.FlowModule.Superuser;
|
||||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.TransactionalFlow;
|
||||
import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
@@ -115,7 +116,9 @@ public final class DomainTransferRejectFlow implements TransactionalFlow {
|
||||
targetId, newDomain.getTransferData(), null, now, domainHistoryKey));
|
||||
// Reopen the autorenew event and poll message that we closed for the implicit transfer. This
|
||||
// may end up recreating the poll message if it was deleted upon the transfer request.
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, END_OF_TIME);
|
||||
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
|
||||
updateAutorenewRecurrenceEndTime(
|
||||
existingDomain, existingRecurring, END_OF_TIME, domainHistory.getDomainHistoryId());
|
||||
// Delete the billing event and poll messages that were written in case the transfer would have
|
||||
// been implicitly server approved.
|
||||
tm().delete(existingDomain.getTransferData().getServerApproveEntities());
|
||||
|
||||
@@ -47,19 +47,23 @@ import google.registry.flows.FlowModule.Superuser;
|
||||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.TransactionalFlow;
|
||||
import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
|
||||
import google.registry.flows.exceptions.AlreadyPendingTransferException;
|
||||
import google.registry.flows.exceptions.InvalidTransferPeriodValueException;
|
||||
import google.registry.flows.exceptions.ObjectAlreadySponsoredException;
|
||||
import google.registry.flows.exceptions.TransferPeriodMustBeOneYearException;
|
||||
import google.registry.flows.exceptions.TransferPeriodZeroAndFeeTransferExtensionException;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainCommand.Transfer;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.fee.FeeTransferCommandExtension;
|
||||
import google.registry.model.domain.fee.FeeTransformResponseExtension;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.domain.superuser.DomainTransferRequestSuperuserExtension;
|
||||
import google.registry.model.domain.token.AllocationTokenExtension;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
@@ -116,6 +120,18 @@ import org.joda.time.DateTime;
|
||||
* @error {@link DomainFlowUtils.PremiumNameBlockedException}
|
||||
* @error {@link DomainFlowUtils.RegistrarMustBeActiveForThisOperationException}
|
||||
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException}
|
||||
*/
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_TRANSFER_REQUEST)
|
||||
public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
@@ -137,6 +153,8 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
@Inject AsyncTaskEnqueuer asyncTaskEnqueuer;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@Inject AllocationTokenFlowUtils allocationTokenFlowUtils;
|
||||
|
||||
@Inject DomainTransferRequestFlow() {}
|
||||
|
||||
@Override
|
||||
@@ -144,12 +162,19 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
extensionManager.register(
|
||||
DomainTransferRequestSuperuserExtension.class,
|
||||
FeeTransferCommandExtension.class,
|
||||
MetadataExtension.class);
|
||||
MetadataExtension.class,
|
||||
AllocationTokenExtension.class);
|
||||
validateRegistrarIsLoggedIn(gainingClientId);
|
||||
verifyRegistrarIsActive(gainingClientId);
|
||||
extensionManager.validate();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
|
||||
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
|
||||
existingDomain,
|
||||
Registry.get(existingDomain.getTld()),
|
||||
gainingClientId,
|
||||
now,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
Optional<DomainTransferRequestSuperuserExtension> superuserExtension =
|
||||
eppInput.getSingleExtension(DomainTransferRequestSuperuserExtension.class);
|
||||
Period period =
|
||||
@@ -168,10 +193,12 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
throw new TransferPeriodZeroAndFeeTransferExtensionException();
|
||||
}
|
||||
// If the period is zero, then there is no fee for the transfer.
|
||||
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
|
||||
Optional<FeesAndCredits> feesAndCredits =
|
||||
(period.getValue() == 0)
|
||||
? Optional.empty()
|
||||
: Optional.of(pricingLogic.getTransferPrice(registry, targetId, now));
|
||||
: Optional.of(
|
||||
pricingLogic.getTransferPrice(registry, targetId, now, existingRecurring));
|
||||
if (feesAndCredits.isPresent()) {
|
||||
validateFeeChallenge(targetId, now, feeTransfer, feesAndCredits.get());
|
||||
}
|
||||
@@ -180,9 +207,12 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
.setId(domainHistoryKey.getId())
|
||||
.setOtherRegistrarId(existingDomain.getCurrentSponsorRegistrarId());
|
||||
DateTime automaticTransferTime =
|
||||
superuserExtension.isPresent()
|
||||
? now.plusDays(superuserExtension.get().getAutomaticTransferLength())
|
||||
: now.plus(registry.getAutomaticTransferLength());
|
||||
superuserExtension
|
||||
.map(
|
||||
domainTransferRequestSuperuserExtension ->
|
||||
now.plusDays(
|
||||
domainTransferRequestSuperuserExtension.getAutomaticTransferLength()))
|
||||
.orElseGet(() -> now.plus(registry.getAutomaticTransferLength()));
|
||||
// If the domain will be in the auto-renew grace period at the moment of transfer, the transfer
|
||||
// will subsume the autorenew, so we don't add the normal extra year from the transfer.
|
||||
// The gaining registrar is still billed for the extra year; the losing registrar will get a
|
||||
@@ -201,6 +231,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
serverApproveNewExpirationTime,
|
||||
domainHistoryKey,
|
||||
existingDomain,
|
||||
existingRecurring,
|
||||
trid,
|
||||
gainingClientId,
|
||||
feesAndCredits.map(FeesAndCredits::getTotalCost),
|
||||
@@ -230,7 +261,11 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
// the poll message if it has no events left. Note that if the automatic transfer succeeds, then
|
||||
// cloneProjectedAtTime() will replace these old autorenew entities with the server approve ones
|
||||
// that we've created in this flow and stored in pendingTransferData.
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, automaticTransferTime);
|
||||
updateAutorenewRecurrenceEndTime(
|
||||
existingDomain,
|
||||
existingRecurring,
|
||||
automaticTransferTime,
|
||||
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()));
|
||||
Domain newDomain =
|
||||
existingDomain
|
||||
.asBuilder()
|
||||
@@ -294,7 +329,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
*
|
||||
* <p>Even if not required, this policy is desirable because it dramatically simplifies the logic
|
||||
* in transfer flows. Registrars appear to never request 2+ year transfers in practice, and they
|
||||
* can always decompose an multi-year transfer into a 1-year transfer followed by a manual renewal
|
||||
* can always decompose a multi-year transfer into a 1-year transfer followed by a manual renewal
|
||||
* afterwards. The <a href="https://tools.ietf.org/html/rfc5731#section-3.2.4">EPP Domain RFC,
|
||||
* section 3.2.4</a> says about EPP transfer periods that "the number of units available MAY be
|
||||
* subject to limits imposed by the server" so we're just limiting the units to one.
|
||||
@@ -339,7 +374,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
private DomainTransferResponse createResponse(
|
||||
Period period, Domain existingDomain, Domain newDomain, DateTime now) {
|
||||
// If the registration were approved this instant, this is what the new expiration would be,
|
||||
// because we cap at 10 years from the moment of approval. This is different than the server
|
||||
// because we cap at 10 years from the moment of approval. This is different from the server
|
||||
// approval new expiration time, which is capped at 10 years from the server approve time.
|
||||
DateTime approveNowExtendedRegistrationTime =
|
||||
computeExDateForApprovalTime(existingDomain, now, period);
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.googlecode.objectify.Key;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||
@@ -110,6 +111,7 @@ public final class DomainTransferUtils {
|
||||
DateTime serverApproveNewExpirationTime,
|
||||
Key<DomainHistory> domainHistoryKey,
|
||||
Domain existingDomain,
|
||||
Recurring existingRecurring,
|
||||
Trid trid,
|
||||
String gainingRegistrarId,
|
||||
Optional<Money> transferCost,
|
||||
@@ -144,7 +146,11 @@ public final class DomainTransferUtils {
|
||||
return builder
|
||||
.add(
|
||||
createGainingClientAutorenewEvent(
|
||||
serverApproveNewExpirationTime, domainHistoryKey, targetId, gainingRegistrarId))
|
||||
existingRecurring,
|
||||
serverApproveNewExpirationTime,
|
||||
domainHistoryKey,
|
||||
targetId,
|
||||
gainingRegistrarId))
|
||||
.add(
|
||||
createGainingClientAutorenewPollMessage(
|
||||
serverApproveNewExpirationTime, domainHistoryKey, targetId, gainingRegistrarId))
|
||||
@@ -239,6 +245,7 @@ public final class DomainTransferUtils {
|
||||
}
|
||||
|
||||
private static BillingEvent.Recurring createGainingClientAutorenewEvent(
|
||||
Recurring existingRecurring,
|
||||
DateTime serverApproveNewExpirationTime,
|
||||
Key<DomainHistory> domainHistoryKey,
|
||||
String targetId,
|
||||
@@ -250,6 +257,8 @@ public final class DomainTransferUtils {
|
||||
.setRegistrarId(gainingRegistrarId)
|
||||
.setEventTime(serverApproveNewExpirationTime)
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.setRenewalPriceBehavior(existingRecurring.getRenewalPriceBehavior())
|
||||
.setRenewalPrice(existingRecurring.getRenewalPrice().orElse(null))
|
||||
.setDomainHistoryId(
|
||||
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
|
||||
.build();
|
||||
|
||||
@@ -30,8 +30,8 @@ import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.CheckData.HostCheck;
|
||||
import google.registry.model.eppoutput.CheckData.HostCheckData;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.host.HostCommand.Check;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.util.Clock;
|
||||
import javax.inject.Inject;
|
||||
@@ -61,8 +61,7 @@ public final class HostCheckFlow implements Flow {
|
||||
extensionManager.validate(); // There are no legal extensions for this flow.
|
||||
ImmutableList<String> hostnames = ((Check) resourceCommand).getTargetIds();
|
||||
verifyTargetIdCount(hostnames, maxChecks);
|
||||
ImmutableSet<String> existingIds =
|
||||
checkResourcesExist(HostResource.class, hostnames, clock.nowUtc());
|
||||
ImmutableSet<String> existingIds = checkResourcesExist(Host.class, hostnames, clock.nowUtc());
|
||||
ImmutableList.Builder<HostCheck> checks = new ImmutableList.Builder<>();
|
||||
for (String hostname : hostnames) {
|
||||
boolean unused = !existingIds.contains(hostname);
|
||||
|
||||
@@ -46,9 +46,9 @@ import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.CreateData.HostCreateData;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.host.HostCommand.Create;
|
||||
import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.index.EppResourceIndex;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
@@ -105,7 +105,7 @@ public final class HostCreateFlow implements TransactionalFlow {
|
||||
extensionManager.validate();
|
||||
Create command = (Create) resourceCommand;
|
||||
DateTime now = tm().getTransactionTime();
|
||||
verifyResourceDoesNotExist(HostResource.class, targetId, now, registrarId);
|
||||
verifyResourceDoesNotExist(Host.class, targetId, now, registrarId);
|
||||
// The superordinate domain of the host object if creating an in-bailiwick host, or null if
|
||||
// creating an external host. This is looked up before we actually create the Host object so
|
||||
// we can detect error conditions earlier.
|
||||
@@ -121,8 +121,8 @@ public final class HostCreateFlow implements TransactionalFlow {
|
||||
? new SubordinateHostMustHaveIpException()
|
||||
: new UnexpectedExternalHostIpException();
|
||||
}
|
||||
HostResource newHost =
|
||||
new HostResource.Builder()
|
||||
Host newHost =
|
||||
new Host.Builder()
|
||||
.setCreationRegistrarId(registrarId)
|
||||
.setPersistedCurrentSponsorRegistrarId(registrarId)
|
||||
.setHostName(targetId)
|
||||
|
||||
@@ -38,8 +38,8 @@ import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.reporting.HistoryEntry.Type;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import javax.inject.Inject;
|
||||
@@ -92,8 +92,8 @@ public final class HostDeleteFlow implements TransactionalFlow {
|
||||
extensionManager.validate();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
validateHostName(targetId);
|
||||
checkLinkedDomains(targetId, now, HostResource.class);
|
||||
HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now);
|
||||
checkLinkedDomains(targetId, now, Host.class);
|
||||
Host existingHost = loadAndVerifyExistence(Host.class, targetId, now);
|
||||
verifyNoDisallowedStatuses(existingHost, DISALLOWED_STATUSES);
|
||||
if (!isSuperuser) {
|
||||
// Hosts transfer with their superordinate domains, so for hosts with a superordinate domain,
|
||||
@@ -104,8 +104,7 @@ public final class HostDeleteFlow implements TransactionalFlow {
|
||||
: existingHost;
|
||||
verifyResourceOwnership(registrarId, owningResource);
|
||||
}
|
||||
HostResource newHost =
|
||||
existingHost.asBuilder().setStatusValues(null).setDeletionTime(now).build();
|
||||
Host newHost = existingHost.asBuilder().setStatusValues(null).setDeletionTime(now).build();
|
||||
if (existingHost.isSubordinate()) {
|
||||
dnsQueue.addHostRefreshTask(existingHost.getHostName());
|
||||
tm().update(
|
||||
|
||||
@@ -30,8 +30,8 @@ import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.host.HostInfoData;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.util.Clock;
|
||||
import javax.inject.Inject;
|
||||
@@ -65,7 +65,7 @@ public final class HostInfoFlow implements Flow {
|
||||
extensionManager.validate(); // There are no legal extensions for this flow.
|
||||
validateHostName(targetId);
|
||||
DateTime now = clock.nowUtc();
|
||||
HostResource host = loadAndVerifyExistence(HostResource.class, targetId, now);
|
||||
Host host = loadAndVerifyExistence(Host.class, targetId, now);
|
||||
ImmutableSet.Builder<StatusValue> statusValues = new ImmutableSet.Builder<>();
|
||||
statusValues.addAll(host.getStatusValues());
|
||||
if (isLinked(host.createVKey(), now)) {
|
||||
|
||||
@@ -52,11 +52,11 @@ import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.host.HostCommand.Update;
|
||||
import google.registry.model.host.HostCommand.Update.AddRemove;
|
||||
import google.registry.model.host.HostCommand.Update.Change;
|
||||
import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.persistence.VKey;
|
||||
@@ -133,7 +133,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
String suppliedNewHostName = change.getFullyQualifiedHostName();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
validateHostName(targetId);
|
||||
HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now);
|
||||
Host existingHost = loadAndVerifyExistence(Host.class, targetId, now);
|
||||
boolean isHostRename = suppliedNewHostName != null;
|
||||
String oldHostName = targetId;
|
||||
String newHostName = firstNonNull(suppliedNewHostName, oldHostName);
|
||||
@@ -148,7 +148,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
EppResource owningResource = firstNonNull(oldSuperordinateDomain, existingHost);
|
||||
verifyUpdateAllowed(
|
||||
command, existingHost, newSuperordinateDomain.orElse(null), owningResource, isHostRename);
|
||||
if (isHostRename && loadAndGetKey(HostResource.class, newHostName, now) != null) {
|
||||
if (isHostRename && loadAndGetKey(Host.class, newHostName, now) != null) {
|
||||
throw new HostAlreadyExistsException(newHostName);
|
||||
}
|
||||
AddRemove add = command.getInnerAdd();
|
||||
@@ -175,7 +175,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
newSuperordinateDomain.isPresent()
|
||||
? newSuperordinateDomain.get().getCurrentSponsorRegistrarId()
|
||||
: owningResource.getPersistedCurrentSponsorRegistrarId();
|
||||
HostResource newHost =
|
||||
Host newHost =
|
||||
existingHost
|
||||
.asBuilder()
|
||||
.setHostName(newHostName)
|
||||
@@ -210,7 +210,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
|
||||
private void verifyUpdateAllowed(
|
||||
Update command,
|
||||
HostResource existingHost,
|
||||
Host existingHost,
|
||||
Domain newSuperordinateDomain,
|
||||
EppResource owningResource,
|
||||
boolean isHostRename)
|
||||
@@ -237,8 +237,8 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
verifyNoDisallowedStatuses(existingHost, DISALLOWED_STATUSES);
|
||||
}
|
||||
|
||||
private void verifyHasIpsIffIsExternal(
|
||||
Update command, HostResource existingHost, HostResource newHost) throws EppException {
|
||||
private void verifyHasIpsIffIsExternal(Update command, Host existingHost, Host newHost)
|
||||
throws EppException {
|
||||
boolean wasSubordinate = existingHost.isSubordinate();
|
||||
boolean willBeSubordinate = newHost.isSubordinate();
|
||||
boolean willBeExternal = !willBeSubordinate;
|
||||
@@ -258,7 +258,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
}
|
||||
}
|
||||
|
||||
private void enqueueTasks(HostResource existingHost, HostResource newHost) {
|
||||
private void enqueueTasks(Host existingHost, Host newHost) {
|
||||
// Only update DNS for subordinate hosts. External hosts have no glue to write, so they
|
||||
// are only written as NS records from the referencing domain.
|
||||
if (existingHost.isSubordinate()) {
|
||||
@@ -277,7 +277,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateSuperordinateDomains(HostResource existingHost, HostResource newHost) {
|
||||
private static void updateSuperordinateDomains(Host existingHost, Host newHost) {
|
||||
if (existingHost.isSubordinate()
|
||||
&& newHost.isSubordinate()
|
||||
&& Objects.equals(
|
||||
|
||||
@@ -41,7 +41,7 @@ public abstract class BackupGroupRoot extends ImmutableObject implements UnsafeS
|
||||
* that this is updated on every save, rather than only in response to an {@code <update>} command
|
||||
*/
|
||||
@XmlTransient
|
||||
// Prevents subclasses from unexpectedly accessing as property (e.g., HostResource), which would
|
||||
// Prevents subclasses from unexpectedly accessing as property (e.g., Host), which would
|
||||
// require an unnecessary non-private setter method.
|
||||
@Access(AccessType.FIELD)
|
||||
@AttributeOverride(name = "lastUpdateTime", column = @Column(name = "updateTimestamp"))
|
||||
|
||||
@@ -23,12 +23,11 @@ import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.index.EppResourceIndex;
|
||||
import google.registry.model.index.EppResourceIndexBucket;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.rde.RdeRevision;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
@@ -55,12 +54,9 @@ public final class EntityClasses {
|
||||
ForeignKeyIndex.ForeignKeyHostIndex.class,
|
||||
GaeUserIdConverter.class,
|
||||
HistoryEntry.class,
|
||||
Host.class,
|
||||
HostHistory.class,
|
||||
HostResource.class,
|
||||
Lock.class,
|
||||
PollMessage.class,
|
||||
PollMessage.Autorenew.class,
|
||||
PollMessage.OneTime.class,
|
||||
RdeRevision.class,
|
||||
Registrar.class,
|
||||
ServerSecret.class);
|
||||
|
||||
@@ -35,7 +35,7 @@ import google.registry.model.EppResource.ResourceWithTransferData;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.HistoryEntryDao;
|
||||
@@ -345,8 +345,8 @@ public final class EppResourceUtils {
|
||||
public static ImmutableSet<VKey<Domain>> getLinkedDomainKeys(
|
||||
VKey<? extends EppResource> key, DateTime now, @Nullable Integer limit) {
|
||||
checkArgument(
|
||||
key.getKind().equals(ContactResource.class) || key.getKind().equals(HostResource.class),
|
||||
"key must be either VKey<ContactResource> or VKey<HostResource>, but it is %s",
|
||||
key.getKind().equals(ContactResource.class) || key.getKind().equals(Host.class),
|
||||
"key must be either VKey<ContactResource> or VKey<Host>, but it is %s",
|
||||
key);
|
||||
boolean isContactKey = key.getKind().equals(ContactResource.class);
|
||||
if (tm().isOfy()) {
|
||||
|
||||
@@ -26,7 +26,7 @@ import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.domain.GracePeriod.GracePeriodHistory;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.domain.secdns.DomainDsDataHistory;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
@@ -68,7 +68,7 @@ public class BulkQueryEntities {
|
||||
DomainLite domainLite,
|
||||
ImmutableSet<GracePeriod> gracePeriods,
|
||||
ImmutableSet<DelegationSignerData> delegationSignerData,
|
||||
ImmutableSet<VKey<HostResource>> nsHosts) {
|
||||
ImmutableSet<VKey<Host>> nsHosts) {
|
||||
Domain.Builder builder = new Domain.Builder();
|
||||
builder.copyFrom(domainLite);
|
||||
builder.setGracePeriods(gracePeriods);
|
||||
@@ -82,7 +82,7 @@ public class BulkQueryEntities {
|
||||
public static DomainHistory assembleDomainHistory(
|
||||
DomainHistoryLite domainHistoryLite,
|
||||
ImmutableSet<DomainDsDataHistory> dsDataHistories,
|
||||
ImmutableSet<VKey<HostResource>> domainHistoryHosts,
|
||||
ImmutableSet<VKey<Host>> domainHistoryHosts,
|
||||
ImmutableSet<GracePeriodHistory> gracePeriodHistories,
|
||||
ImmutableSet<DomainTransactionRecord> transactionRecords) {
|
||||
DomainHistory.Builder builder = new DomainHistory.Builder();
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.model.bulkquery;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.io.Serializable;
|
||||
import javax.persistence.Access;
|
||||
@@ -44,8 +44,8 @@ public class DomainHistoryHost implements Serializable {
|
||||
return new DomainHistoryId(domainHistoryDomainRepoId, domainHistoryHistoryRevisionId);
|
||||
}
|
||||
|
||||
public VKey<HostResource> getHostVKey() {
|
||||
return VKey.create(HostResource.class, hostRepoId);
|
||||
public VKey<Host> getHostVKey() {
|
||||
return VKey.create(Host.class, hostRepoId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package google.registry.model.bulkquery;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.io.Serializable;
|
||||
import javax.persistence.Access;
|
||||
@@ -40,8 +40,8 @@ public class DomainHost implements Serializable {
|
||||
return domainRepoId;
|
||||
}
|
||||
|
||||
public VKey<HostResource> getHostVKey() {
|
||||
return VKey.create(HostResource.class, hostRepoId);
|
||||
public VKey<Host> getHostVKey() {
|
||||
return VKey.create(Host.class, hostRepoId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.console;
|
||||
|
||||
/** Permissions that users may have in the UI, either per-registrar or globally. */
|
||||
public enum ConsolePermission {
|
||||
/** Add, update, or remove other console users. */
|
||||
MANAGE_USERS,
|
||||
/** Add, update, or remove registrars. */
|
||||
MANAGE_REGISTRARS,
|
||||
/** Manage related registrars, e.g. when one registrar owns another. */
|
||||
MANAGE_ACCREDITATION,
|
||||
/** Set up the EPP connection (e.g. certs). */
|
||||
CONFIGURE_EPP_CONNECTION,
|
||||
/** Retrieve the unredacted registrant email from a domain. */
|
||||
GET_REGISTRANT_EMAIL,
|
||||
/** Suspend a domain for compliance (non-URS) reasons. */
|
||||
SUSPEND_DOMAIN,
|
||||
/** Suspend a domain for the Uniform Rapid Suspension process. */
|
||||
SUSPEND_DOMAIN_URS,
|
||||
/** Download a list of domains under management. */
|
||||
DOWNLOAD_DOMAINS,
|
||||
/** Change the password for a registrar. */
|
||||
CHANGE_NOMULUS_PASSWORD,
|
||||
/** View all possible TLDs. */
|
||||
VIEW_TLD_PORTFOLIO,
|
||||
/** Onboarding the registrar(s) to extra programs like registry locking. */
|
||||
ONBOARD_ADDITIONAL_PROGRAMS,
|
||||
/** Execute arbitrary EPP commands through the UI. */
|
||||
EXECUTE_EPP_COMMANDS,
|
||||
/** Send messages to the registry support team. */
|
||||
CONTACT_SUPPORT,
|
||||
/** Access billing and payment details for a registrar. */
|
||||
ACCESS_BILLING_DETAILS,
|
||||
/** Access any available documentation about the registry. */
|
||||
ACCESS_DOCUMENTATION,
|
||||
/** Change the documentation available in the UI about the registry. */
|
||||
MANAGE_DOCUMENTATION,
|
||||
/** Viewing the onboarding status of a registrar on a TLD. */
|
||||
CHECK_ONBOARDING_STATUS,
|
||||
/** View premium and/or reserved lists for a TLD. */
|
||||
VIEW_PREMIUM_RESERVED_LISTS,
|
||||
/** Sign and/or change legal documents like RRAs. */
|
||||
UPLOAD_CONTRACTS,
|
||||
/** Viewing legal documents like RRAs. */
|
||||
ACCESS_CONTRACTS,
|
||||
/** View analytics on operations and billing data (possibly TBD). */
|
||||
VIEW_OPERATIONAL_DATA,
|
||||
/** Create announcements that can be displayed in the UI. */
|
||||
SEND_ANNOUNCEMENTS,
|
||||
/** View announcements in the UI. */
|
||||
VIEW_ANNOUNCEMENTS,
|
||||
/** Viewing a record of actions performed in the UI for a particular registrar. */
|
||||
VIEW_ACTIVITY_LOG,
|
||||
/** View and perform registry locks. */
|
||||
REGISTRY_LOCK
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.console;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
* Definitions of the {@link ConsolePermission} sets that each role contains.
|
||||
*
|
||||
* <p>Note: within the "registry" and "registrar" groupings, permissions expand hierarchically,
|
||||
* where each role has all the permissions of the role below it plus some others.
|
||||
*/
|
||||
public class ConsoleRoleDefinitions {
|
||||
|
||||
/** Permissions for a registry support agent. */
|
||||
static final ImmutableSet<ConsolePermission> SUPPORT_AGENT_PERMISSIONS =
|
||||
ImmutableSet.of(
|
||||
ConsolePermission.MANAGE_USERS,
|
||||
ConsolePermission.MANAGE_ACCREDITATION,
|
||||
ConsolePermission.CONFIGURE_EPP_CONNECTION,
|
||||
ConsolePermission.DOWNLOAD_DOMAINS,
|
||||
ConsolePermission.VIEW_TLD_PORTFOLIO,
|
||||
ConsolePermission.ONBOARD_ADDITIONAL_PROGRAMS,
|
||||
ConsolePermission.CONTACT_SUPPORT,
|
||||
ConsolePermission.ACCESS_BILLING_DETAILS,
|
||||
ConsolePermission.ACCESS_DOCUMENTATION,
|
||||
ConsolePermission.SUSPEND_DOMAIN_URS,
|
||||
ConsolePermission.CHECK_ONBOARDING_STATUS,
|
||||
ConsolePermission.VIEW_PREMIUM_RESERVED_LISTS,
|
||||
ConsolePermission.UPLOAD_CONTRACTS,
|
||||
ConsolePermission.ACCESS_CONTRACTS,
|
||||
ConsolePermission.VIEW_OPERATIONAL_DATA,
|
||||
ConsolePermission.SEND_ANNOUNCEMENTS,
|
||||
ConsolePermission.VIEW_ANNOUNCEMENTS,
|
||||
ConsolePermission.VIEW_ACTIVITY_LOG,
|
||||
ConsolePermission.REGISTRY_LOCK);
|
||||
|
||||
/** Permissions for a registry support lead. */
|
||||
static final ImmutableSet<ConsolePermission> SUPPORT_LEAD_PERMISSIONS =
|
||||
new ImmutableSet.Builder<ConsolePermission>()
|
||||
.addAll(SUPPORT_AGENT_PERMISSIONS)
|
||||
.add(
|
||||
ConsolePermission.MANAGE_REGISTRARS,
|
||||
ConsolePermission.GET_REGISTRANT_EMAIL,
|
||||
ConsolePermission.SUSPEND_DOMAIN,
|
||||
ConsolePermission.EXECUTE_EPP_COMMANDS,
|
||||
ConsolePermission.MANAGE_DOCUMENTATION)
|
||||
.build();
|
||||
|
||||
/** Permissions for a registry full-time employee. */
|
||||
static final ImmutableSet<ConsolePermission> FTE_PERMISSIONS =
|
||||
new ImmutableSet.Builder<ConsolePermission>()
|
||||
.addAll(SUPPORT_LEAD_PERMISSIONS)
|
||||
.add(ConsolePermission.CHANGE_NOMULUS_PASSWORD)
|
||||
.build();
|
||||
|
||||
/** Permissions for a registrar partner account manager. */
|
||||
static final ImmutableSet<ConsolePermission> ACCOUNT_MANAGER_PERMISSIONS =
|
||||
ImmutableSet.of(
|
||||
ConsolePermission.DOWNLOAD_DOMAINS,
|
||||
ConsolePermission.VIEW_TLD_PORTFOLIO,
|
||||
ConsolePermission.CONTACT_SUPPORT,
|
||||
ConsolePermission.ACCESS_DOCUMENTATION,
|
||||
ConsolePermission.VIEW_PREMIUM_RESERVED_LISTS,
|
||||
ConsolePermission.VIEW_OPERATIONAL_DATA,
|
||||
ConsolePermission.VIEW_ANNOUNCEMENTS);
|
||||
|
||||
/** Permissions for a registrar partner account manager that can perform registry locks. */
|
||||
static final ImmutableSet<ConsolePermission> ACCOUNT_MANAGER_WITH_REGISTRY_LOCK_PERMISSIONS =
|
||||
new ImmutableSet.Builder<ConsolePermission>()
|
||||
.addAll(ACCOUNT_MANAGER_PERMISSIONS)
|
||||
.add(ConsolePermission.REGISTRY_LOCK)
|
||||
.build();
|
||||
|
||||
/** Permissions for the tech contact of a registrar. */
|
||||
static final ImmutableSet<ConsolePermission> TECH_CONTACT_PERMISSIONS =
|
||||
new ImmutableSet.Builder<ConsolePermission>()
|
||||
.addAll(ACCOUNT_MANAGER_WITH_REGISTRY_LOCK_PERMISSIONS)
|
||||
.add(
|
||||
ConsolePermission.MANAGE_ACCREDITATION,
|
||||
ConsolePermission.CONFIGURE_EPP_CONNECTION,
|
||||
ConsolePermission.CHANGE_NOMULUS_PASSWORD,
|
||||
ConsolePermission.ONBOARD_ADDITIONAL_PROGRAMS,
|
||||
ConsolePermission.EXECUTE_EPP_COMMANDS,
|
||||
ConsolePermission.ACCESS_BILLING_DETAILS,
|
||||
ConsolePermission.CHECK_ONBOARDING_STATUS,
|
||||
ConsolePermission.UPLOAD_CONTRACTS,
|
||||
ConsolePermission.ACCESS_CONTRACTS,
|
||||
ConsolePermission.VIEW_ACTIVITY_LOG)
|
||||
.build();
|
||||
|
||||
/** Permissions for the primary registrar contact. */
|
||||
static final ImmutableSet<ConsolePermission> PRIMARY_CONTACT_PERMISSIONS =
|
||||
new ImmutableSet.Builder<ConsolePermission>()
|
||||
.addAll(TECH_CONTACT_PERMISSIONS)
|
||||
.add(ConsolePermission.MANAGE_USERS)
|
||||
.build();
|
||||
|
||||
private ConsoleRoleDefinitions() {}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.console;
|
||||
|
||||
import static google.registry.model.console.ConsoleRoleDefinitions.FTE_PERMISSIONS;
|
||||
import static google.registry.model.console.ConsoleRoleDefinitions.SUPPORT_AGENT_PERMISSIONS;
|
||||
import static google.registry.model.console.ConsoleRoleDefinitions.SUPPORT_LEAD_PERMISSIONS;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/** Roles for registry employees that apply across all registrars. */
|
||||
public enum GlobalRole {
|
||||
|
||||
/** The user has no global role, i.e. they're a registrar partner. */
|
||||
NONE(ImmutableSet.of()),
|
||||
/** The user is a registry support agent. */
|
||||
SUPPORT_AGENT(SUPPORT_AGENT_PERMISSIONS),
|
||||
/** The user is a registry support lead. */
|
||||
SUPPORT_LEAD(SUPPORT_LEAD_PERMISSIONS),
|
||||
/** The user is a registry full-time employee. */
|
||||
FTE(FTE_PERMISSIONS);
|
||||
|
||||
private final ImmutableSet<ConsolePermission> permissions;
|
||||
|
||||
GlobalRole(ImmutableSet<ConsolePermission> permissions) {
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
public boolean hasPermission(ConsolePermission permission) {
|
||||
return permissions.contains(permission);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.console;
|
||||
|
||||
import static google.registry.model.console.ConsoleRoleDefinitions.ACCOUNT_MANAGER_PERMISSIONS;
|
||||
import static google.registry.model.console.ConsoleRoleDefinitions.ACCOUNT_MANAGER_WITH_REGISTRY_LOCK_PERMISSIONS;
|
||||
import static google.registry.model.console.ConsoleRoleDefinitions.PRIMARY_CONTACT_PERMISSIONS;
|
||||
import static google.registry.model.console.ConsoleRoleDefinitions.TECH_CONTACT_PERMISSIONS;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/** Roles for registrar partners that apply to only one registrar. */
|
||||
public enum RegistrarRole {
|
||||
|
||||
/** The user is a standard account manager at a registrar. */
|
||||
ACCOUNT_MANAGER(ACCOUNT_MANAGER_PERMISSIONS),
|
||||
/** The user is an account manager that can perform registry locks. */
|
||||
ACCOUNT_MANAGER_WITH_REGISTRY_LOCK(ACCOUNT_MANAGER_WITH_REGISTRY_LOCK_PERMISSIONS),
|
||||
/** The user is a technical contact of a registrar. */
|
||||
TECH_CONTACT(TECH_CONTACT_PERMISSIONS),
|
||||
/** The user is the primary contact at a registrar. */
|
||||
PRIMARY_CONTACT(PRIMARY_CONTACT_PERMISSIONS);
|
||||
|
||||
private final ImmutableSet<ConsolePermission> permissions;
|
||||
|
||||
RegistrarRole(ImmutableSet<ConsolePermission> permissions) {
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
public boolean hasPermission(ConsolePermission permission) {
|
||||
return permissions.contains(permission);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.console;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static com.google.common.io.BaseEncoding.base64;
|
||||
import static google.registry.model.registrar.Registrar.checkValidEmail;
|
||||
import static google.registry.util.PasswordUtils.SALT_SUPPLIER;
|
||||
import static google.registry.util.PasswordUtils.hashPassword;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
|
||||
/** A console user, either a registry employee or a registrar partner. */
|
||||
public class User extends ImmutableObject implements Buildable {
|
||||
|
||||
/** Autogenerated unique ID of this user. */
|
||||
private long id;
|
||||
|
||||
/** GAIA ID associated with the user in question. */
|
||||
private String gaiaId;
|
||||
|
||||
/** Email address of the user in question. */
|
||||
private String emailAddress;
|
||||
|
||||
/** Roles (which grant permissions) associated with this user. */
|
||||
private UserRoles userRoles;
|
||||
|
||||
/**
|
||||
* A hashed password that exists iff this contact is registry-lock-enabled. The hash is a base64
|
||||
* encoded SHA256 string.
|
||||
*/
|
||||
String registryLockPasswordHash;
|
||||
|
||||
/** Randomly generated hash salt. */
|
||||
String registryLockPasswordSalt;
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getGaiaId() {
|
||||
return gaiaId;
|
||||
}
|
||||
|
||||
public String getEmailAddress() {
|
||||
return emailAddress;
|
||||
}
|
||||
|
||||
public UserRoles getUserRoles() {
|
||||
return userRoles;
|
||||
}
|
||||
|
||||
public boolean hasRegistryLockPassword() {
|
||||
return !isNullOrEmpty(registryLockPasswordHash) && !isNullOrEmpty(registryLockPasswordSalt);
|
||||
}
|
||||
|
||||
public boolean verifyRegistryLockPassword(String registryLockPassword) {
|
||||
if (isNullOrEmpty(registryLockPassword)
|
||||
|| isNullOrEmpty(registryLockPasswordSalt)
|
||||
|| isNullOrEmpty(registryLockPasswordHash)) {
|
||||
return false;
|
||||
}
|
||||
return hashPassword(registryLockPassword, registryLockPasswordSalt)
|
||||
.equals(registryLockPasswordHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the user has the registry lock permission on any registrar or globally.
|
||||
*
|
||||
* <p>If so, they should be allowed to (re)set their registry lock password.
|
||||
*/
|
||||
public boolean hasAnyRegistryLockPermission() {
|
||||
if (userRoles == null) {
|
||||
return false;
|
||||
}
|
||||
if (userRoles.isAdmin() || userRoles.hasGlobalPermission(ConsolePermission.REGISTRY_LOCK)) {
|
||||
return true;
|
||||
}
|
||||
return userRoles.getRegistrarRoles().values().stream()
|
||||
.anyMatch(role -> role.hasPermission(ConsolePermission.REGISTRY_LOCK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
/** Builder for constructing immutable {@link User} objects. */
|
||||
public static class Builder extends Buildable.Builder<User> {
|
||||
|
||||
public Builder() {}
|
||||
|
||||
public Builder(User user) {
|
||||
super(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User build() {
|
||||
checkArgumentNotNull(getInstance().gaiaId, "Gaia ID cannot be null");
|
||||
checkArgumentNotNull(getInstance().emailAddress, "Email address cannot be null");
|
||||
checkArgumentNotNull(getInstance().userRoles, "User roles cannot be null");
|
||||
return super.build();
|
||||
}
|
||||
|
||||
public Builder setGaiaId(String gaiaId) {
|
||||
checkArgument(!isNullOrEmpty(gaiaId), "Gaia ID cannot be null or empty");
|
||||
getInstance().gaiaId = gaiaId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setEmailAddress(String emailAddress) {
|
||||
getInstance().emailAddress = checkValidEmail(emailAddress);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setUserRoles(UserRoles userRoles) {
|
||||
checkArgumentNotNull(userRoles, "User roles cannot be null");
|
||||
getInstance().userRoles = userRoles;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder removeRegistryLockPassword() {
|
||||
getInstance().registryLockPasswordHash = null;
|
||||
getInstance().registryLockPasswordSalt = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRegistryLockPassword(String registryLockPassword) {
|
||||
checkArgument(
|
||||
getInstance().hasAnyRegistryLockPermission(), "User has no registry lock permission");
|
||||
checkArgument(
|
||||
!getInstance().hasRegistryLockPassword(), "User already has a password, remove it first");
|
||||
checkArgument(
|
||||
!isNullOrEmpty(registryLockPassword), "Registry lock password was null or empty");
|
||||
getInstance().registryLockPasswordSalt = base64().encode(SALT_SUPPLIER.get());
|
||||
getInstance().registryLockPasswordHash =
|
||||
hashPassword(registryLockPassword, getInstance().registryLockPasswordSalt);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.console;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Contains the global and per-registrar roles for a given user.
|
||||
*
|
||||
* <p>See <a href="https://go/nomulus-console-authz">go/nomulus-console-authz</a> for more
|
||||
* information.
|
||||
*/
|
||||
public class UserRoles extends ImmutableObject implements Buildable {
|
||||
|
||||
/** Whether the user is a global admin, who has access to everything. */
|
||||
private boolean isAdmin = false;
|
||||
|
||||
/**
|
||||
* The global role (e.g. {@link GlobalRole#SUPPORT_AGENT}) that the user has across all
|
||||
* registrars.
|
||||
*/
|
||||
private GlobalRole globalRole = GlobalRole.NONE;
|
||||
|
||||
/** Any per-registrar roles that this user may have. */
|
||||
private Map<String, RegistrarRole> registrarRoles = ImmutableMap.of();
|
||||
|
||||
/** Whether the user is a global admin, who has access to everything. */
|
||||
public boolean isAdmin() {
|
||||
return isAdmin;
|
||||
}
|
||||
|
||||
/**
|
||||
* The global role (e.g. {@link GlobalRole#SUPPORT_AGENT}) that the user has across all
|
||||
* registrars.
|
||||
*/
|
||||
public GlobalRole getGlobalRole() {
|
||||
return globalRole;
|
||||
}
|
||||
|
||||
/** Any per-registrar roles that this user may have. */
|
||||
public Map<String, RegistrarRole> getRegistrarRoles() {
|
||||
return ImmutableMap.copyOf(registrarRoles);
|
||||
}
|
||||
|
||||
/** If the user has the given permission either globally or on the given registrar. */
|
||||
public boolean hasPermission(String registrarId, ConsolePermission permission) {
|
||||
if (hasGlobalPermission(permission)) {
|
||||
return true;
|
||||
}
|
||||
if (!registrarRoles.containsKey(registrarId)) {
|
||||
return false;
|
||||
}
|
||||
return registrarRoles.get(registrarId).hasPermission(permission);
|
||||
}
|
||||
|
||||
/** If the user has the given permission globally across all registrars. */
|
||||
public boolean hasGlobalPermission(ConsolePermission permission) {
|
||||
return isAdmin || globalRole.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
/** Builder for constructing immutable {@link UserRoles} objects. */
|
||||
public static class Builder extends Buildable.Builder<UserRoles> {
|
||||
|
||||
public Builder() {}
|
||||
|
||||
private Builder(UserRoles userRoles) {
|
||||
super(userRoles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserRoles build() {
|
||||
// Users should only have a global role or per-registrar roles, not both
|
||||
checkArgument(
|
||||
getInstance().globalRole.equals(GlobalRole.NONE)
|
||||
|| getInstance().registrarRoles.isEmpty(),
|
||||
"Users cannot have both global and per-registrar roles");
|
||||
return super.build();
|
||||
}
|
||||
|
||||
public Builder setIsAdmin(boolean isAdmin) {
|
||||
getInstance().isAdmin = isAdmin;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setGlobalRole(GlobalRole globalRole) {
|
||||
checkArgumentNotNull(globalRole, "Global role cannot be null");
|
||||
getInstance().globalRole = globalRole;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRegistrarRoles(Map<String, RegistrarRole> registrarRoles) {
|
||||
checkArgumentNotNull(registrarRoles, "Registrar roles map cannot be null");
|
||||
getInstance().registrarRoles = ImmutableMap.copyOf(registrarRoles);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import google.registry.model.EppResource.ForeignKeyedEppResource;
|
||||
import google.registry.model.annotations.ExternalMessagingName;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithStringVKey;
|
||||
import java.util.Set;
|
||||
@@ -89,14 +89,14 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
|
||||
})
|
||||
@Access(AccessType.PROPERTY)
|
||||
@Column(name = "host_repo_id")
|
||||
public Set<VKey<HostResource>> getNsHosts() {
|
||||
return super.nsHosts;
|
||||
public Set<VKey<Host>> getNsHosts() {
|
||||
return nsHosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of {@link GracePeriod} associated with the domain.
|
||||
*
|
||||
* <p>This is the getter method specific for Hibernate to access the field so it is set to
|
||||
* <p>This is the getter method specific for Hibernate to access the field, so it is set to
|
||||
* private. The caller can use the public {@link #getGracePeriods()} to get the grace periods.
|
||||
*
|
||||
* <p>Note that we need to set `insertable = false, updatable = false` for @JoinColumn, otherwise
|
||||
@@ -104,10 +104,7 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
|
||||
* deleting the whole entry from the table when the {@link GracePeriod} is removed from the set.
|
||||
*/
|
||||
@Access(AccessType.PROPERTY)
|
||||
@OneToMany(
|
||||
cascade = {CascadeType.ALL},
|
||||
fetch = FetchType.EAGER,
|
||||
orphanRemoval = true)
|
||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
|
||||
@JoinColumn(
|
||||
name = "domainRepoId",
|
||||
referencedColumnName = "repoId",
|
||||
@@ -121,7 +118,7 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
|
||||
/**
|
||||
* Returns the set of {@link DelegationSignerData} associated with the domain.
|
||||
*
|
||||
* <p>This is the getter method specific for Hibernate to access the field so it is set to
|
||||
* <p>This is the getter method specific for Hibernate to access the field, so it is set to
|
||||
* private. The caller can use the public {@link #getDsData()} to get the DS data.
|
||||
*
|
||||
* <p>Note that we need to set `insertable = false, updatable = false` for @JoinColumn, otherwise
|
||||
@@ -130,10 +127,7 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
|
||||
* the set.
|
||||
*/
|
||||
@Access(AccessType.PROPERTY)
|
||||
@OneToMany(
|
||||
cascade = {CascadeType.ALL},
|
||||
fetch = FetchType.EAGER,
|
||||
orphanRemoval = true)
|
||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
|
||||
@JoinColumn(
|
||||
name = "domainRepoId",
|
||||
referencedColumnName = "repoId",
|
||||
@@ -182,10 +176,9 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
|
||||
}
|
||||
|
||||
public Builder copyFrom(DomainBase domainBase) {
|
||||
this.getInstance().copyUpdateTimestamp(domainBase);
|
||||
return this.setAuthInfo(domainBase.getAuthInfo())
|
||||
.setAutorenewPollMessage(
|
||||
domainBase.getAutorenewPollMessage(), domainBase.getAutorenewPollMessageHistoryId())
|
||||
getInstance().copyUpdateTimestamp(domainBase);
|
||||
return setAuthInfo(domainBase.getAuthInfo())
|
||||
.setAutorenewPollMessage(domainBase.getAutorenewPollMessage())
|
||||
.setAutorenewBillingEvent(domainBase.getAutorenewBillingEvent())
|
||||
.setAutorenewEndTime(domainBase.getAutorenewEndTime())
|
||||
.setContacts(domainBase.getContacts())
|
||||
@@ -210,7 +203,8 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
|
||||
.setSubordinateHosts(domainBase.getSubordinateHosts())
|
||||
.setStatusValues(domainBase.getStatusValues())
|
||||
.setTransferData(domainBase.getTransferData())
|
||||
.setDnsRefreshRequestTime(domainBase.getDnsRefreshRequestTime());
|
||||
.setDnsRefreshRequestTime(domainBase.getDnsRefreshRequestTime())
|
||||
.setCurrentPackageToken(domainBase.getCurrentPackageToken().orElse(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,8 +56,9 @@ import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.launch.LaunchNotice;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.tld.Registry;
|
||||
@@ -128,7 +129,7 @@ public class DomainBase extends EppResource
|
||||
@Index String tld;
|
||||
|
||||
/** References to hosts that are the nameservers for the domain. */
|
||||
@EmptySetToNull @Index @Transient Set<VKey<HostResource>> nsHosts;
|
||||
@EmptySetToNull @Index @Transient Set<VKey<Host>> nsHosts;
|
||||
|
||||
/** Contacts. */
|
||||
VKey<ContactResource> adminContact;
|
||||
@@ -215,13 +216,6 @@ public class DomainBase extends EppResource
|
||||
@Column(name = "autorenew_poll_message_id")
|
||||
VKey<PollMessage.Autorenew> autorenewPollMessage;
|
||||
|
||||
/**
|
||||
* History record for the autorenew poll message.
|
||||
*
|
||||
* <p>Here so we can restore the original ofy key from sql.
|
||||
*/
|
||||
@Ignore Long autorenewPollMessageHistoryId;
|
||||
|
||||
/** The unexpired grace periods for this domain (some of which may not be active yet). */
|
||||
@Transient Set<GracePeriod> gracePeriods;
|
||||
|
||||
@@ -282,6 +276,9 @@ public class DomainBase extends EppResource
|
||||
// TODO(mcilwain): Start using this field once we are further along in the DB migration.
|
||||
@Ignore DateTime dnsRefreshRequestTime;
|
||||
|
||||
/** The {@link AllocationToken} for the package this domain is currently a part of. */
|
||||
@Nullable VKey<AllocationToken> currentPackageToken;
|
||||
|
||||
/**
|
||||
* Returns the DNS refresh request time iff this domain's DNS needs refreshing, otherwise absent.
|
||||
*/
|
||||
@@ -318,10 +315,6 @@ public class DomainBase extends EppResource
|
||||
return autorenewPollMessage;
|
||||
}
|
||||
|
||||
public Long getAutorenewPollMessageHistoryId() {
|
||||
return autorenewPollMessageHistoryId;
|
||||
}
|
||||
|
||||
public ImmutableSet<GracePeriod> getGracePeriods() {
|
||||
return nullToEmptyImmutableCopy(gracePeriods);
|
||||
}
|
||||
@@ -330,6 +323,10 @@ public class DomainBase extends EppResource
|
||||
return smdId;
|
||||
}
|
||||
|
||||
public Optional<VKey<AllocationToken>> getCurrentPackageToken() {
|
||||
return Optional.ofNullable(currentPackageToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the autorenew end time if there is one, otherwise empty.
|
||||
*
|
||||
@@ -372,13 +369,13 @@ public class DomainBase extends EppResource
|
||||
return idnTableName;
|
||||
}
|
||||
|
||||
public ImmutableSet<VKey<HostResource>> getNameservers() {
|
||||
public ImmutableSet<VKey<Host>> getNameservers() {
|
||||
return nullToEmptyImmutableCopy(nsHosts);
|
||||
}
|
||||
|
||||
// Hibernate needs this in order to populate nsHosts but no one else should ever use it
|
||||
@SuppressWarnings("UnusedMethod")
|
||||
private void setNsHosts(Set<VKey<HostResource>> nsHosts) {
|
||||
private void setNsHosts(Set<VKey<Host>> nsHosts) {
|
||||
this.nsHosts = forceEmptyToNull(nsHosts);
|
||||
}
|
||||
|
||||
@@ -501,9 +498,7 @@ public class DomainBase extends EppResource
|
||||
// Set the speculatively-written new autorenew events as the domain's autorenew
|
||||
// events.
|
||||
.setAutorenewBillingEvent(transferData.getServerApproveAutorenewEvent())
|
||||
.setAutorenewPollMessage(
|
||||
transferData.getServerApproveAutorenewPollMessage(),
|
||||
transferData.getServerApproveAutorenewPollMessageHistoryId());
|
||||
.setAutorenewPollMessage(transferData.getServerApproveAutorenewPollMessage());
|
||||
if (transferData.getTransferPeriod().getValue() == 1) {
|
||||
// Set the grace period using a key to the prescheduled transfer billing event. Not using
|
||||
// GracePeriod.forBillingEvent() here in order to avoid the actual Datastore fetch.
|
||||
@@ -602,7 +597,7 @@ public class DomainBase extends EppResource
|
||||
return tm().transact(
|
||||
() ->
|
||||
tm().loadByKeys(getNameservers()).values().stream()
|
||||
.map(HostResource::getHostName)
|
||||
.map(Host::getHostName)
|
||||
.collect(toImmutableSortedSet(Ordering.natural())));
|
||||
}
|
||||
|
||||
@@ -787,32 +782,32 @@ public class DomainBase extends EppResource
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setNameservers(VKey<HostResource> nameserver) {
|
||||
public B setNameservers(VKey<Host> nameserver) {
|
||||
getInstance().nsHosts = ImmutableSet.of(nameserver);
|
||||
getInstance().resetUpdateTimestamp();
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setNameservers(ImmutableSet<VKey<HostResource>> nameservers) {
|
||||
public B setNameservers(ImmutableSet<VKey<Host>> nameservers) {
|
||||
getInstance().nsHosts = forceEmptyToNull(nameservers);
|
||||
getInstance().resetUpdateTimestamp();
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B addNameserver(VKey<HostResource> nameserver) {
|
||||
public B addNameserver(VKey<Host> nameserver) {
|
||||
return addNameservers(ImmutableSet.of(nameserver));
|
||||
}
|
||||
|
||||
public B addNameservers(ImmutableSet<VKey<HostResource>> nameservers) {
|
||||
public B addNameservers(ImmutableSet<VKey<Host>> nameservers) {
|
||||
return setNameservers(
|
||||
ImmutableSet.copyOf(Sets.union(getInstance().getNameservers(), nameservers)));
|
||||
}
|
||||
|
||||
public B removeNameserver(VKey<HostResource> nameserver) {
|
||||
public B removeNameserver(VKey<Host> nameserver) {
|
||||
return removeNameservers(ImmutableSet.of(nameserver));
|
||||
}
|
||||
|
||||
public B removeNameservers(ImmutableSet<VKey<HostResource>> nameservers) {
|
||||
public B removeNameservers(ImmutableSet<VKey<Host>> nameservers) {
|
||||
return setNameservers(
|
||||
ImmutableSet.copyOf(difference(getInstance().getNameservers(), nameservers)));
|
||||
}
|
||||
@@ -878,11 +873,8 @@ public class DomainBase extends EppResource
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setAutorenewPollMessage(
|
||||
@Nullable VKey<PollMessage.Autorenew> autorenewPollMessage,
|
||||
@Nullable Long autorenewPollMessageHistoryId) {
|
||||
public B setAutorenewPollMessage(@Nullable VKey<PollMessage.Autorenew> autorenewPollMessage) {
|
||||
getInstance().autorenewPollMessage = autorenewPollMessage;
|
||||
getInstance().autorenewPollMessageHistoryId = autorenewPollMessageHistoryId;
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
@@ -938,5 +930,10 @@ public class DomainBase extends EppResource
|
||||
getInstance().lastTransferTime = lastTransferTime;
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setCurrentPackageToken(@Nullable VKey<AllocationToken> currentPackageToken) {
|
||||
getInstance().currentPackageToken = currentPackageToken;
|
||||
return thisCastToDerived();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import google.registry.model.eppinput.ResourceCommand.ResourceCheck;
|
||||
import google.registry.model.eppinput.ResourceCommand.ResourceCreateOrChange;
|
||||
import google.registry.model.eppinput.ResourceCommand.ResourceUpdate;
|
||||
import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Set;
|
||||
@@ -126,7 +126,7 @@ public class DomainCommand {
|
||||
Set<String> nameserverFullyQualifiedHostNames;
|
||||
|
||||
/** Resolved keys to hosts that are the nameservers for the domain. */
|
||||
@XmlTransient Set<VKey<HostResource>> nameservers;
|
||||
@XmlTransient Set<VKey<Host>> nameservers;
|
||||
|
||||
/** Foreign keyed associated contacts for the domain (other than registrant). */
|
||||
@XmlElement(name = "contact")
|
||||
@@ -156,7 +156,7 @@ public class DomainCommand {
|
||||
return nullToEmptyImmutableCopy(nameserverFullyQualifiedHostNames);
|
||||
}
|
||||
|
||||
public ImmutableSet<VKey<HostResource>> getNameservers() {
|
||||
public ImmutableSet<VKey<Host>> getNameservers() {
|
||||
return nullToEmptyImmutableCopy(nameservers);
|
||||
}
|
||||
|
||||
@@ -349,7 +349,7 @@ public class DomainCommand {
|
||||
Set<String> nameserverFullyQualifiedHostNames;
|
||||
|
||||
/** Resolved keys to hosts that are the nameservers for the domain. */
|
||||
@XmlTransient Set<VKey<HostResource>> nameservers;
|
||||
@XmlTransient Set<VKey<Host>> nameservers;
|
||||
|
||||
/** Foreign keyed associated contacts for the domain (other than registrant). */
|
||||
@XmlElement(name = "contact")
|
||||
@@ -363,7 +363,7 @@ public class DomainCommand {
|
||||
return nullSafeImmutableCopy(nameserverFullyQualifiedHostNames);
|
||||
}
|
||||
|
||||
public ImmutableSet<VKey<HostResource>> getNameservers() {
|
||||
public ImmutableSet<VKey<Host>> getNameservers() {
|
||||
return nullToEmptyImmutableCopy(nameservers);
|
||||
}
|
||||
|
||||
@@ -413,13 +413,13 @@ public class DomainCommand {
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<VKey<HostResource>> linkHosts(
|
||||
Set<String> fullyQualifiedHostNames, DateTime now) throws InvalidReferencesException {
|
||||
private static Set<VKey<Host>> linkHosts(Set<String> fullyQualifiedHostNames, DateTime now)
|
||||
throws InvalidReferencesException {
|
||||
if (fullyQualifiedHostNames == null) {
|
||||
return null;
|
||||
}
|
||||
return ImmutableSet.copyOf(
|
||||
loadByForeignKeysCached(fullyQualifiedHostNames, HostResource.class, now).values());
|
||||
loadByForeignKeysCached(fullyQualifiedHostNames, Host.class, now).values());
|
||||
}
|
||||
|
||||
private static Set<DesignatedContact> linkContacts(
|
||||
|
||||
@@ -26,7 +26,7 @@ import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||
import google.registry.model.domain.GracePeriod.GracePeriodHistory;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.domain.secdns.DomainDsDataHistory;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.VKey;
|
||||
@@ -113,7 +113,7 @@ public class DomainHistory extends HistoryEntry {
|
||||
})
|
||||
@ImmutableObject.EmptySetToNull
|
||||
@Column(name = "host_repo_id")
|
||||
Set<VKey<HostResource>> nsHosts;
|
||||
Set<VKey<Host>> nsHosts;
|
||||
|
||||
@DoNotCompare
|
||||
@OneToMany(
|
||||
@@ -221,8 +221,8 @@ public class DomainHistory extends HistoryEntry {
|
||||
return new DomainHistoryId(getDomainRepoId(), getId());
|
||||
}
|
||||
|
||||
/** Returns keys to the {@link HostResource} that are the nameservers for the domain. */
|
||||
public Set<VKey<HostResource>> getNsHosts() {
|
||||
/** Returns keys to the {@link Host} that are the nameservers for the domain. */
|
||||
public Set<VKey<Host>> getNsHosts() {
|
||||
return nsHosts;
|
||||
}
|
||||
|
||||
@@ -406,7 +406,7 @@ public class DomainHistory extends HistoryEntry {
|
||||
|
||||
public DomainHistory buildAndAssemble(
|
||||
ImmutableSet<DomainDsDataHistory> dsDataHistories,
|
||||
ImmutableSet<VKey<HostResource>> domainHistoryHosts,
|
||||
ImmutableSet<VKey<Host>> domainHistoryHosts,
|
||||
ImmutableSet<GracePeriodHistory> gracePeriodHistories,
|
||||
ImmutableSet<DomainTransactionRecord> transactionRecords) {
|
||||
DomainHistory instance = super.build();
|
||||
|
||||
@@ -24,8 +24,8 @@ import google.registry.model.contact.ContactBase;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.host.HostBase;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.translators.EnumToAttributeAdapter.EppEnum;
|
||||
import google.registry.model.translators.StatusValueAdapter;
|
||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
@@ -135,7 +135,7 @@ public enum StatusValue implements EppEnum {
|
||||
DomainBase.class,
|
||||
Domain.class,
|
||||
HostBase.class,
|
||||
HostResource.class),
|
||||
Host.class),
|
||||
NONE,
|
||||
DOMAINS(DomainBase.class, Domain.class);
|
||||
|
||||
|
||||
+7
-7
@@ -27,7 +27,7 @@ import javax.persistence.AccessType;
|
||||
/**
|
||||
* A persistable Host resource including mutable and non-mutable fields.
|
||||
*
|
||||
* <p>The {@link javax.persistence.Id} of the HostResource is the repoId.
|
||||
* <p>The {@link javax.persistence.Id} of the Host is the repoId.
|
||||
*/
|
||||
@ReportedOn
|
||||
@Entity
|
||||
@@ -54,7 +54,7 @@ import javax.persistence.AccessType;
|
||||
@ExternalMessagingName("host")
|
||||
@WithStringVKey
|
||||
@Access(AccessType.FIELD) // otherwise it'll use the default if the repoId (property)
|
||||
public class HostResource extends HostBase implements ForeignKeyedEppResource {
|
||||
public class Host extends HostBase implements ForeignKeyedEppResource {
|
||||
|
||||
@Override
|
||||
@javax.persistence.Id
|
||||
@@ -64,8 +64,8 @@ public class HostResource extends HostBase implements ForeignKeyedEppResource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public VKey<HostResource> createVKey() {
|
||||
return VKey.create(HostResource.class, getRepoId(), Key.create(this));
|
||||
public VKey<Host> createVKey() {
|
||||
return VKey.create(Host.class, getRepoId(), Key.create(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,11 +73,11 @@ public class HostResource extends HostBase implements ForeignKeyedEppResource {
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
/** A builder for constructing {@link HostResource}, since it is immutable. */
|
||||
public static class Builder extends HostBase.Builder<HostResource, Builder> {
|
||||
/** A builder for constructing {@link Host}, since it is immutable. */
|
||||
public static class Builder extends HostBase.Builder<Host, Builder> {
|
||||
public Builder() {}
|
||||
|
||||
private Builder(HostResource instance) {
|
||||
private Builder(Host instance) {
|
||||
super(instance);
|
||||
}
|
||||
|
||||
@@ -126,8 +126,7 @@ public class HostBase extends EppResource {
|
||||
@Override
|
||||
public VKey<? extends HostBase> createVKey() {
|
||||
throw new UnsupportedOperationException(
|
||||
"HostBase is not an actual persisted entity you can create a key to;"
|
||||
+ " use HostResource instead");
|
||||
"HostBase is not an actual persisted entity you can create a key to; use Host instead");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
|
||||
@@ -29,26 +29,26 @@ import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/** A collection of {@link HostResource} commands. */
|
||||
/** A collection of {@link Host} commands. */
|
||||
public class HostCommand {
|
||||
|
||||
/** The fields on "chgType" from <a href="http://tools.ietf.org/html/rfc5732">RFC5732</a>. */
|
||||
@XmlTransient
|
||||
abstract static class HostCreateOrChange extends AbstractSingleResourceCommand
|
||||
implements ResourceCreateOrChange<HostResource.Builder> {
|
||||
implements ResourceCreateOrChange<Host.Builder> {
|
||||
public String getFullyQualifiedHostName() {
|
||||
return getTargetId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A create command for a {@link HostResource}, mapping "createType" from <a
|
||||
* A create command for a {@link Host}, mapping "createType" from <a
|
||||
* href="http://tools.ietf.org/html/rfc5732">RFC5732</a>.
|
||||
*/
|
||||
@XmlType(propOrder = {"targetId", "inetAddresses"})
|
||||
@XmlRootElement
|
||||
public static class Create extends HostCreateOrChange
|
||||
implements ResourceCreateOrChange<HostResource.Builder> {
|
||||
implements ResourceCreateOrChange<Host.Builder> {
|
||||
/** IP Addresses for this host. Can be null if this is an external host. */
|
||||
@XmlElement(name = "addr")
|
||||
Set<InetAddress> inetAddresses;
|
||||
@@ -58,23 +58,22 @@ public class HostCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/** A delete command for a {@link HostResource}. */
|
||||
/** A delete command for a {@link Host}. */
|
||||
@XmlRootElement
|
||||
public static class Delete extends AbstractSingleResourceCommand {}
|
||||
|
||||
/** An info request for a {@link HostResource}. */
|
||||
/** An info request for a {@link Host}. */
|
||||
@XmlRootElement
|
||||
public static class Info extends AbstractSingleResourceCommand {}
|
||||
|
||||
/** A check request for {@link HostResource}. */
|
||||
/** A check request for {@link Host}. */
|
||||
@XmlRootElement
|
||||
public static class Check extends ResourceCheck {}
|
||||
|
||||
/** An update to a {@link HostResource}. */
|
||||
/** An update to a {@link Host}. */
|
||||
@XmlRootElement
|
||||
@XmlType(propOrder = {"targetId", "innerAdd", "innerRemove", "innerChange"})
|
||||
public static class Update extends ResourceUpdate
|
||||
<Update.AddRemove, HostResource.Builder, Update.Change> {
|
||||
public static class Update extends ResourceUpdate<Update.AddRemove, Host.Builder, Update.Change> {
|
||||
|
||||
@XmlElement(name = "chg")
|
||||
protected Change innerChange;
|
||||
|
||||
@@ -59,7 +59,7 @@ import javax.persistence.PostLoad;
|
||||
@IdClass(HostHistoryId.class)
|
||||
public class HostHistory extends HistoryEntry implements UnsafeSerializable {
|
||||
|
||||
// Store HostBase instead of HostResource so we don't pick up its @Id
|
||||
// Store HostBase instead of Host so we don't pick up its @Id
|
||||
// Nullable for the sake of pre-Registry-3.0 history objects
|
||||
@DoNotCompare @Nullable HostBase hostBase;
|
||||
|
||||
@@ -74,7 +74,7 @@ public class HostHistory extends HistoryEntry implements UnsafeSerializable {
|
||||
/** This method is private because it is only used by Hibernate. */
|
||||
@SuppressWarnings("unused")
|
||||
private void setHostRepoId(String hostRepoId) {
|
||||
parent = Key.create(HostResource.class, hostRepoId);
|
||||
parent = Key.create(Host.class, hostRepoId);
|
||||
}
|
||||
|
||||
@Id
|
||||
@@ -99,9 +99,9 @@ public class HostHistory extends HistoryEntry implements UnsafeSerializable {
|
||||
return Optional.ofNullable(hostBase);
|
||||
}
|
||||
|
||||
/** The key to the {@link google.registry.model.host.HostResource} this is based off of. */
|
||||
public VKey<HostResource> getParentVKey() {
|
||||
return VKey.create(HostResource.class, getHostRepoId());
|
||||
/** The key to the {@link Host} this is based off of. */
|
||||
public VKey<Host> getParentVKey() {
|
||||
return VKey.create(Host.class, getHostRepoId());
|
||||
}
|
||||
|
||||
/** Creates a {@link VKey} instance for this entity. */
|
||||
@@ -113,7 +113,7 @@ public class HostHistory extends HistoryEntry implements UnsafeSerializable {
|
||||
|
||||
@Override
|
||||
public Optional<? extends EppResource> getResourceAtPointInTime() {
|
||||
return getHostBase().map(hostBase -> new HostResource.Builder().copyFrom(hostBase).build());
|
||||
return getHostBase().map(hostBase -> new Host.Builder().copyFrom(hostBase).build());
|
||||
}
|
||||
|
||||
@PostLoad
|
||||
@@ -210,7 +210,7 @@ public class HostHistory extends HistoryEntry implements UnsafeSerializable {
|
||||
}
|
||||
|
||||
public Builder setHostRepoId(String hostRepoId) {
|
||||
getInstance().parent = Key.create(HostResource.class, hostRepoId);
|
||||
getInstance().parent = Key.create(Host.class, hostRepoId);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ import google.registry.model.annotations.DeleteAfterMigration;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
@@ -80,10 +80,10 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
|
||||
@Entity
|
||||
public static class ForeignKeyDomainIndex extends ForeignKeyIndex<Domain> {}
|
||||
|
||||
/** The {@link ForeignKeyIndex} type for {@link HostResource} entities. */
|
||||
/** The {@link ForeignKeyIndex} type for {@link Host} entities. */
|
||||
@ReportedOn
|
||||
@Entity
|
||||
public static class ForeignKeyHostIndex extends ForeignKeyIndex<HostResource> {}
|
||||
public static class ForeignKeyHostIndex extends ForeignKeyIndex<Host> {}
|
||||
|
||||
private static final ImmutableBiMap<
|
||||
Class<? extends EppResource>, Class<? extends ForeignKeyIndex<?>>>
|
||||
@@ -91,14 +91,14 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
|
||||
ImmutableBiMap.of(
|
||||
ContactResource.class, ForeignKeyContactIndex.class,
|
||||
Domain.class, ForeignKeyDomainIndex.class,
|
||||
HostResource.class, ForeignKeyHostIndex.class);
|
||||
Host.class, ForeignKeyHostIndex.class);
|
||||
|
||||
private static final ImmutableMap<Class<? extends EppResource>, String>
|
||||
RESOURCE_CLASS_TO_FKI_PROPERTY =
|
||||
ImmutableMap.of(
|
||||
ContactResource.class, "contactId",
|
||||
Domain.class, "fullyQualifiedDomainName",
|
||||
HostResource.class, "fullyQualifiedHostName");
|
||||
Host.class, "fullyQualifiedHostName");
|
||||
|
||||
@Id String foreignKey;
|
||||
|
||||
@@ -106,7 +106,7 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
|
||||
* The deletion time of this {@link ForeignKeyIndex}.
|
||||
*
|
||||
* <p>This will generally be equal to the deletion time of {@link #topReference}. However, in the
|
||||
* case of a {@link HostResource} that was renamed, this field will hold the time of the rename.
|
||||
* case of a {@link Host} that was renamed, this field will hold the time of the rename.
|
||||
*/
|
||||
@Index DateTime deletionTime;
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
* via Objectify.
|
||||
*
|
||||
* <p>All first class entities are represented as a resource class - {@link
|
||||
* google.registry.model.domain.Domain}, {@link google.registry.model.host.HostResource}, {@link
|
||||
* google.registry.model.domain.Domain}, {@link google.registry.model.host.Host}, {@link
|
||||
* google.registry.model.contact.ContactResource}, and {@link
|
||||
* google.registry.model.registrar.Registrar}. Resource objects are written in a single shared
|
||||
* entity group per TLD. All commands that operate on those entities are grouped in a "Command"
|
||||
|
||||
@@ -20,18 +20,12 @@ import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.EntitySubclass;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
import com.googlecode.objectify.annotation.Ignore;
|
||||
import com.googlecode.objectify.annotation.Index;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.UnsafeSerializable;
|
||||
import google.registry.model.annotations.ExternalMessagingName;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.model.annotations.OfyIdAllocation;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.contact.ContactHistory.ContactHistoryId;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
@@ -40,9 +34,9 @@ import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||
import google.registry.model.domain.DomainRenewData;
|
||||
import google.registry.model.eppoutput.EppResponse.ResponseData;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.host.HostHistory.HostHistoryId;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse;
|
||||
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
||||
import google.registry.model.poll.PendingActionNotificationResponse.HostPendingActionNotificationResponse;
|
||||
@@ -55,16 +49,19 @@ import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithLongVKey;
|
||||
import google.registry.util.NullIgnoringCollectionBuilder;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.AttributeOverride;
|
||||
import javax.persistence.AttributeOverrides;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.DiscriminatorColumn;
|
||||
import javax.persistence.DiscriminatorValue;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.PostLoad;
|
||||
import javax.persistence.Table;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
@@ -76,10 +73,10 @@ import org.joda.time.DateTime;
|
||||
* retained for historical purposes, poll messages are truly deleted once they have been delivered
|
||||
* and ACKed.
|
||||
*
|
||||
* <p>Poll messages are parented off of the {@link HistoryEntry} that resulted in their creation.
|
||||
* This means that poll messages are contained in the Datastore entity group of the parent {@link
|
||||
* EppResource} (which can be a domain, contact, or host). It is thus possible to perform a strongly
|
||||
* consistent query to find all poll messages associated with a given EPP resource.
|
||||
* <p>Poll messages have a reference to the revision id of the {@link HistoryEntry} that resulted in
|
||||
* their creation or re-creation, in case of transfer cancellation/rejection. The revision ids are
|
||||
* not used directly by the Java code, but their presence in the database makes it easier to
|
||||
* diagnose potential issues related to poll messages.
|
||||
*
|
||||
* <p>Poll messages are identified externally by registrars using the format defined in {@link
|
||||
* PollMessageExternalKeyConverter}.
|
||||
@@ -88,16 +85,10 @@ import org.joda.time.DateTime;
|
||||
* Command</a>
|
||||
*/
|
||||
@Entity
|
||||
@ReportedOn
|
||||
@ExternalMessagingName("message")
|
||||
@javax.persistence.Entity
|
||||
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
|
||||
@DiscriminatorColumn(name = "type")
|
||||
@javax.persistence.Table(
|
||||
indexes = {
|
||||
@javax.persistence.Index(columnList = "registrar_id"),
|
||||
@javax.persistence.Index(columnList = "eventTime")
|
||||
})
|
||||
@Table(indexes = {@Index(columnList = "registrar_id"), @Index(columnList = "eventTime")})
|
||||
public abstract class PollMessage extends ImmutableObject
|
||||
implements Buildable, TransferServerApproveEntity, UnsafeSerializable {
|
||||
|
||||
@@ -105,12 +96,12 @@ public abstract class PollMessage extends ImmutableObject
|
||||
public enum Type {
|
||||
DOMAIN(1L, Domain.class),
|
||||
CONTACT(2L, ContactResource.class),
|
||||
HOST(3L, HostResource.class);
|
||||
HOST(3L, Host.class);
|
||||
|
||||
private final long id;
|
||||
private final Class<?> clazz;
|
||||
private final Class<? extends EppResource> clazz;
|
||||
|
||||
Type(long id, Class<?> clazz) {
|
||||
Type(long id, Class<? extends EppResource> clazz) {
|
||||
this.id = id;
|
||||
this.clazz = clazz;
|
||||
}
|
||||
@@ -124,58 +115,40 @@ public abstract class PollMessage extends ImmutableObject
|
||||
}
|
||||
|
||||
/** Returns the class of the underlying resource for the poll message type. */
|
||||
public Class<?> getResourceClass() {
|
||||
public Class<? extends EppResource> getResourceClass() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type specified by the identifer, {@code Optional.empty()} if the id is out of
|
||||
* range.
|
||||
*/
|
||||
public static Optional<Type> fromId(long id) {
|
||||
for (Type val : values()) {
|
||||
if (val.id == id) {
|
||||
return Optional.of(val);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/** Entity id. */
|
||||
@Id
|
||||
@javax.persistence.Id
|
||||
@OfyIdAllocation
|
||||
@Column(name = "poll_message_id")
|
||||
Long id;
|
||||
|
||||
/** The registrar that this poll message will be delivered to. */
|
||||
@Index
|
||||
@Column(name = "registrar_id", nullable = false)
|
||||
String clientId;
|
||||
|
||||
/** The time when the poll message should be delivered. May be in the future. */
|
||||
@Index
|
||||
@Column(nullable = false)
|
||||
DateTime eventTime;
|
||||
|
||||
/** Human readable message that will be returned with this poll message. */
|
||||
/** Human-readable message that will be returned with this poll message. */
|
||||
@Column(name = "message")
|
||||
String msg;
|
||||
|
||||
// TODO(b/456803336): Replace these fields with {Domain,Contact,Host}HistoryId objects.
|
||||
String domainRepoId;
|
||||
|
||||
@Ignore String domainRepoId;
|
||||
String contactRepoId;
|
||||
|
||||
@Ignore String contactRepoId;
|
||||
String hostRepoId;
|
||||
|
||||
@Ignore String hostRepoId;
|
||||
Long domainHistoryRevisionId;
|
||||
|
||||
@Ignore Long domainHistoryRevisionId;
|
||||
Long contactHistoryRevisionId;
|
||||
|
||||
@Ignore Long contactHistoryRevisionId;
|
||||
|
||||
@Ignore Long hostHistoryRevisionId;
|
||||
Long hostHistoryRevisionId;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
@@ -216,7 +189,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||
/**
|
||||
* Returns the host repo id.
|
||||
*
|
||||
* <p>This may only be used on a HostResource poll event.
|
||||
* <p>This may only be used on a Host poll event.
|
||||
*/
|
||||
public String getHostRepoId() {
|
||||
checkArgument(getType() == Type.DOMAIN);
|
||||
@@ -228,32 +201,22 @@ public abstract class PollMessage extends ImmutableObject
|
||||
* the resource.
|
||||
*/
|
||||
public String getResourceName() {
|
||||
return domainRepoId != null
|
||||
? domainRepoId
|
||||
: (contactRepoId != null ? contactRepoId : hostRepoId);
|
||||
return domainRepoId != null ? domainRepoId : contactRepoId != null ? contactRepoId : hostRepoId;
|
||||
}
|
||||
|
||||
/** Gets the underlying history revision id, regardless of the type of the resource. */
|
||||
public Long getHistoryRevisionId() {
|
||||
return domainHistoryRevisionId != null
|
||||
? domainHistoryRevisionId
|
||||
: (contactHistoryRevisionId != null ? contactHistoryRevisionId : hostHistoryRevisionId);
|
||||
: contactHistoryRevisionId != null ? contactHistoryRevisionId : hostHistoryRevisionId;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return domainRepoId != null ? Type.DOMAIN : (contactRepoId != null ? Type.CONTACT : Type.HOST);
|
||||
return domainRepoId != null ? Type.DOMAIN : contactRepoId != null ? Type.CONTACT : Type.HOST;
|
||||
}
|
||||
|
||||
public abstract ImmutableList<ResponseData> getResponseData();
|
||||
|
||||
@Override
|
||||
public abstract VKey<? extends PollMessage> createVKey();
|
||||
|
||||
/** Static VKey factory method for use by VKeyTranslatorFactory. */
|
||||
public static VKey<PollMessage> createVKey(Key<PollMessage> key) {
|
||||
return VKey.create(PollMessage.class, key.getId(), key);
|
||||
}
|
||||
|
||||
/** Override Buildable.asBuilder() to give this method stronger typing. */
|
||||
@Override
|
||||
public abstract Builder<?, ?> asBuilder();
|
||||
@@ -318,9 +281,11 @@ public abstract class PollMessage extends ImmutableObject
|
||||
// Set the appropriate field based on the history entry type.
|
||||
if (history instanceof DomainHistory) {
|
||||
return setDomainHistoryId(((DomainHistory) history).getDomainHistoryId());
|
||||
} else if (history instanceof ContactHistory) {
|
||||
}
|
||||
if (history instanceof ContactHistory) {
|
||||
return setContactHistoryId(((ContactHistory) history).getContactHistoryId());
|
||||
} else if (history instanceof HostHistory) {
|
||||
}
|
||||
if (history instanceof HostHistory) {
|
||||
return setHostHistoryId(((HostHistory) history).getHostHistoryId());
|
||||
}
|
||||
return thisCastToDerived();
|
||||
@@ -330,7 +295,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||
* Given an array containing pairs of objects, verifies that both members of exactly one of the
|
||||
* pairs is non-null.
|
||||
*/
|
||||
private boolean exactlyOnePairNonNull(Object... pairs) {
|
||||
private static boolean exactlyOnePairNonNull(Object... pairs) {
|
||||
int count = 0;
|
||||
checkArgument(pairs.length % 2 == 0, "Odd number of arguments provided.");
|
||||
for (int i = 0; i < pairs.length; i += 2) {
|
||||
@@ -360,9 +325,9 @@ public abstract class PollMessage extends ImmutableObject
|
||||
instance.contactHistoryRevisionId,
|
||||
instance.hostRepoId,
|
||||
instance.hostHistoryRevisionId),
|
||||
"the repo id and history revision id must be defined for exactly one of domain, "
|
||||
+ "contact or host: "
|
||||
+ instance);
|
||||
"The repo id and history revision id must be defined for exactly one of domain, "
|
||||
+ "contact or host: %s",
|
||||
instance);
|
||||
return super.build();
|
||||
}
|
||||
}
|
||||
@@ -372,13 +337,11 @@ public abstract class PollMessage extends ImmutableObject
|
||||
*
|
||||
* <p>One-time poll messages are deleted from Datastore once they have been delivered and ACKed.
|
||||
*/
|
||||
@EntitySubclass(index = false)
|
||||
@javax.persistence.Entity
|
||||
@Entity
|
||||
@DiscriminatorValue("ONE_TIME")
|
||||
@WithLongVKey(compositeKey = true)
|
||||
public static class OneTime extends PollMessage {
|
||||
|
||||
@Ignore
|
||||
@Embedded
|
||||
@AttributeOverrides({
|
||||
@AttributeOverride(
|
||||
@@ -399,7 +362,6 @@ public abstract class PollMessage extends ImmutableObject
|
||||
})
|
||||
PendingActionNotificationResponse pendingActionNotificationResponse;
|
||||
|
||||
@Ignore
|
||||
@Embedded
|
||||
@AttributeOverrides({
|
||||
@AttributeOverride(
|
||||
@@ -420,35 +382,21 @@ public abstract class PollMessage extends ImmutableObject
|
||||
})
|
||||
TransferResponse transferResponse;
|
||||
|
||||
@Ignore
|
||||
@Column(name = "transfer_response_domain_name")
|
||||
String fullyQualifiedDomainName;
|
||||
|
||||
@Ignore
|
||||
@Column(name = "transfer_response_domain_expiration_time")
|
||||
DateTime extendedRegistrationExpirationTime;
|
||||
|
||||
@Ignore
|
||||
@Column(name = "transfer_response_contact_id")
|
||||
String contactId;
|
||||
|
||||
@Ignore
|
||||
@Column(name = "transfer_response_host_id")
|
||||
String hostId;
|
||||
|
||||
@Override
|
||||
public VKey<OneTime> createVKey() {
|
||||
return VKey.create(OneTime.class, getId(), Key.create(this));
|
||||
}
|
||||
|
||||
/** Converts an unspecialized VKey<PollMessage> to a VKey of the derived class. */
|
||||
public static @Nullable VKey<OneTime> convertVKey(@Nullable VKey<? extends PollMessage> key) {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
Key<OneTime> ofyKey =
|
||||
Key.create(key.getOfyKey().getParent(), OneTime.class, key.getOfyKey().getId());
|
||||
return VKey.create(OneTime.class, key.getSqlKey(), ofyKey);
|
||||
return VKey.createSql(OneTime.class, getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -525,8 +473,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||
/** A builder for {@link OneTime} since it is immutable. */
|
||||
public static class Builder extends PollMessage.Builder<OneTime, Builder> {
|
||||
|
||||
public Builder() {
|
||||
}
|
||||
public Builder() {}
|
||||
|
||||
private Builder(OneTime instance) {
|
||||
super(instance);
|
||||
@@ -587,14 +534,13 @@ public abstract class PollMessage extends ImmutableObject
|
||||
}
|
||||
|
||||
/**
|
||||
* An auto-renew poll message which recurs annually.
|
||||
* An autorenew poll message which recurs annually.
|
||||
*
|
||||
* <p>Auto-renew poll messages are not deleted until the registration of their parent domain has
|
||||
* been canceled, because there will always be a speculative renewal for next year until that
|
||||
* happens.
|
||||
*/
|
||||
@EntitySubclass(index = false)
|
||||
@javax.persistence.Entity
|
||||
@Entity
|
||||
@DiscriminatorValue("AUTORENEW")
|
||||
@WithLongVKey(compositeKey = true)
|
||||
public static class Autorenew extends PollMessage {
|
||||
@@ -604,7 +550,6 @@ public abstract class PollMessage extends ImmutableObject
|
||||
String targetId;
|
||||
|
||||
/** The autorenew recurs annually between {@link #eventTime} and this time. */
|
||||
@Index
|
||||
DateTime autorenewEndTime;
|
||||
|
||||
public String getTargetId() {
|
||||
@@ -617,25 +562,14 @@ public abstract class PollMessage extends ImmutableObject
|
||||
|
||||
@Override
|
||||
public VKey<Autorenew> createVKey() {
|
||||
return VKey.create(Autorenew.class, getId(), Key.create(this));
|
||||
}
|
||||
|
||||
/** Converts an unspecialized VKey<PollMessage> to a VKey of the derived class. */
|
||||
public static @Nullable VKey<Autorenew> convertVKey(VKey<? extends PollMessage> key) {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
Key<Autorenew> ofyKey =
|
||||
Key.create(key.getOfyKey().getParent(), Autorenew.class, key.getOfyKey().getId());
|
||||
return VKey.create(Autorenew.class, key.getSqlKey(), ofyKey);
|
||||
return VKey.createSql(Autorenew.class, getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<ResponseData> getResponseData() {
|
||||
// Note that the event time is when the auto-renew occured, so the expiration time in the
|
||||
// Note that the event time is when the autorenew occurred, so the expiration time in the
|
||||
// response should be 1 year past that, since it denotes the new expiration time.
|
||||
return ImmutableList.of(
|
||||
DomainRenewData.create(getTargetId(), getEventTime().plusYears(1)));
|
||||
return ImmutableList.of(DomainRenewData.create(getTargetId(), getEventTime().plusYears(1)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+16
-39
@@ -14,48 +14,38 @@
|
||||
|
||||
package google.registry.model.poll;
|
||||
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A converter between external key strings for {@link PollMessage}s (i.e. what registrars use to
|
||||
* identify and ACK them) and Datastore keys to the resource.
|
||||
* identify and ACK them) and {@link VKey}s to the resource.
|
||||
*
|
||||
* <p>The format of the key string is A-B-C-D-E-F as follows:
|
||||
* <p>The format of the key string is A-B as follows:
|
||||
*
|
||||
* <pre>
|
||||
* A = EppResource.typeId (decimal)
|
||||
* B = EppResource.repoId prefix (STRING)
|
||||
* C = EppResource.repoId suffix (STRING)
|
||||
* D = HistoryEntry.id (decimal)
|
||||
* E = PollMessage.id (decimal)
|
||||
* F = PollMessage.eventTime (decimal, year only)
|
||||
* A = PollMessage.id (decimal)
|
||||
* B = PollMessage.eventTime (decimal, year only)
|
||||
* </pre>
|
||||
*
|
||||
* <p>A typical poll message ID might thus look like: 1-FE0F22-TLD-10071463070-10072612074-2018
|
||||
* <p>A typical poll message ID might thus look like: 10072612074-2018
|
||||
*/
|
||||
public class PollMessageExternalKeyConverter {
|
||||
public final class PollMessageExternalKeyConverter {
|
||||
|
||||
/** An exception thrown when an external key cannot be parsed. */
|
||||
public static class PollMessageExternalKeyParseException extends RuntimeException {}
|
||||
public static class PollMessageExternalKeyParseException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 9026397649048831777L;
|
||||
}
|
||||
|
||||
/** Returns an external poll message ID for the given poll message. */
|
||||
public static String makePollMessageExternalId(PollMessage pollMessage) {
|
||||
return String.format(
|
||||
"%d-%s-%d-%d-%d",
|
||||
pollMessage.getType().getId(),
|
||||
pollMessage.getResourceName(),
|
||||
pollMessage.getHistoryRevisionId(),
|
||||
pollMessage.getId(),
|
||||
pollMessage.getEventTime().getYear());
|
||||
return String.format("%d-%d", pollMessage.getId(), pollMessage.getEventTime().getYear());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Objectify Key to a PollMessage corresponding with the external ID.
|
||||
* Returns a {@link VKey} to a {@link PollMessage} corresponding with the external ID.
|
||||
*
|
||||
* <p>Note that the year field that is included at the end of the poll message isn't actually used
|
||||
* for anything; it exists solely to create unique externally visible IDs for autorenews. We thus
|
||||
@@ -66,26 +56,13 @@ public class PollMessageExternalKeyConverter {
|
||||
*/
|
||||
public static VKey<PollMessage> parsePollMessageExternalId(String externalKey) {
|
||||
List<String> idComponents = Splitter.on('-').splitToList(externalKey);
|
||||
if (idComponents.size() != 6) {
|
||||
if (idComponents.size() != 2) {
|
||||
throw new PollMessageExternalKeyParseException();
|
||||
}
|
||||
try {
|
||||
Class<?> resourceClazz =
|
||||
PollMessage.Type.fromId(Long.parseLong(idComponents.get(0)))
|
||||
.orElseThrow(() -> new PollMessageExternalKeyParseException())
|
||||
.getResourceClass();
|
||||
return VKey.from(
|
||||
Key.create(
|
||||
Key.create(
|
||||
Key.create(
|
||||
null,
|
||||
resourceClazz,
|
||||
String.format("%s-%s", idComponents.get(1), idComponents.get(2))),
|
||||
HistoryEntry.class,
|
||||
Long.parseLong(idComponents.get(3))),
|
||||
PollMessage.class,
|
||||
Long.parseLong(idComponents.get(4))));
|
||||
// Note that idComponents.get(5) is entirely ignored; we never use the year field internally.
|
||||
Long id = Long.parseLong(idComponents.get(0));
|
||||
return VKey.createSql(PollMessage.class, id);
|
||||
// Note that idComponents.get(1) is entirely ignored; we never use the year field internally.
|
||||
} catch (NumberFormatException e) {
|
||||
throw new PollMessageExternalKeyParseException();
|
||||
}
|
||||
|
||||
@@ -964,7 +964,7 @@ public class Registrar extends ImmutableObject
|
||||
}
|
||||
|
||||
/** Verifies that the email address in question is not null and has a valid format. */
|
||||
static String checkValidEmail(String email) {
|
||||
public static String checkValidEmail(String email) {
|
||||
checkNotNull(email, "Provided email was null");
|
||||
try {
|
||||
InternetAddress internetAddress = new InternetAddress(email, true);
|
||||
|
||||
@@ -44,10 +44,10 @@ import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.host.HostBase;
|
||||
import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.host.HostHistory.HostHistoryId;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -389,7 +389,7 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
|
||||
if (parentKind.equals(getKind(Domain.class))) {
|
||||
resultEntity =
|
||||
new DomainHistory.Builder().copyFrom(this).setDomainRepoId(parent.getName()).build();
|
||||
} else if (parentKind.equals(getKind(HostResource.class))) {
|
||||
} else if (parentKind.equals(getKind(Host.class))) {
|
||||
resultEntity =
|
||||
new HostHistory.Builder().copyFrom(this).setHostRepoId(parent.getName()).build();
|
||||
} else if (parentKind.equals(getKind(ContactResource.class))) {
|
||||
@@ -413,7 +413,7 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
|
||||
DomainHistory.class,
|
||||
new DomainHistoryId(repoId, id),
|
||||
Key.create(parent, DomainHistory.class, id));
|
||||
} else if (parentKind.equals(getKind(HostResource.class))) {
|
||||
} else if (parentKind.equals(getKind(Host.class))) {
|
||||
return VKey.create(
|
||||
HostHistory.class,
|
||||
new HostHistoryId(repoId, id),
|
||||
|
||||
@@ -29,8 +29,8 @@ import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import java.util.Comparator;
|
||||
@@ -55,7 +55,7 @@ public class HistoryEntryDao {
|
||||
ContactHistory.class,
|
||||
Domain.class,
|
||||
DomainHistory.class,
|
||||
HostResource.class,
|
||||
Host.class,
|
||||
HostHistory.class);
|
||||
|
||||
public static ImmutableMap<Class<? extends HistoryEntry>, String> REPO_ID_FIELD_NAMES =
|
||||
|
||||
@@ -73,7 +73,7 @@ public class PremiumListDao {
|
||||
}
|
||||
|
||||
/**
|
||||
* In-memory price cache for for a given premium list revision and domain label.
|
||||
* In-memory price cache for a given premium list revision and domain label.
|
||||
*
|
||||
* <p>Note that premium list revision ids are globally unique, so this cache is specific to a
|
||||
* given premium list. Premium list entries might not be present, as indicated by the Optional
|
||||
|
||||
@@ -90,12 +90,13 @@ public final class ReservedList
|
||||
*
|
||||
* <p>We need to persist the list entries, but only on the initial insert (not on update) since
|
||||
* the entries themselves never get changed, so we only annotate it with {@link PostPersist}, not
|
||||
* {@link PostUpdate}.
|
||||
* PostUpdate.
|
||||
*/
|
||||
@PostPersist
|
||||
void postPersist() {
|
||||
if (reservedListMap != null) {
|
||||
reservedListMap.values().stream()
|
||||
reservedListMap
|
||||
.values()
|
||||
.forEach(
|
||||
entry -> {
|
||||
// We can safely change the revision id since it's "Insignificant".
|
||||
|
||||
@@ -20,7 +20,10 @@ import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static google.registry.persistence.transaction.QueryComposer.Comparator.EQ;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.CacheUtils;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import java.util.Map;
|
||||
@@ -81,9 +84,26 @@ public class ClaimsList extends ImmutableObject {
|
||||
* <p>This field requires special treatment since we want to lazy load it. We have to remove it
|
||||
* from the immutability contract so we can modify it after construction and we have to handle the
|
||||
* database processing on our own so we can detach it after load.
|
||||
*
|
||||
* <p>Unlike the cache below, this is guaranteed to contain all mappings from labels to claim keys
|
||||
* if it is not null.
|
||||
*/
|
||||
@Insignificant @Transient ImmutableMap<String, String> labelsToKeys;
|
||||
|
||||
/**
|
||||
* A not-necessarily-complete cache of labels to claim keys.
|
||||
*
|
||||
* <p>At any point in time this might or might not contain none, some, or all of the mappings from
|
||||
* labels to claim keys. Exists so that repeated calls to {@link #getClaimKey(String)} can be
|
||||
* quick.
|
||||
*
|
||||
* <p>Note: this cache has no expiration because while claims list revisions can be added over
|
||||
* time, each instance of a claims list is immutable.
|
||||
*/
|
||||
@Insignificant @Transient @VisibleForTesting
|
||||
final LoadingCache<String, Optional<String>> claimKeyCache =
|
||||
CacheUtils.newCacheBuilder().build(this::getClaimKeyUncached);
|
||||
|
||||
@PreRemove
|
||||
void preRemove() {
|
||||
jpaTm()
|
||||
@@ -139,18 +159,7 @@ public class ClaimsList extends ImmutableObject {
|
||||
* entries and cache them locally.
|
||||
*/
|
||||
public Optional<String> getClaimKey(String label) {
|
||||
if (labelsToKeys != null) {
|
||||
return Optional.ofNullable(labelsToKeys.get(label));
|
||||
}
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.createQueryComposer(ClaimsEntry.class)
|
||||
.where("revisionId", EQ, revisionId)
|
||||
.where("domainLabel", EQ, label)
|
||||
.first()
|
||||
.map(ClaimsEntry::getClaimKey));
|
||||
return claimKeyCache.get(label);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,6 +201,27 @@ public class ClaimsList extends ImmutableObject {
|
||||
return labelsToKeys.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the claim key for a given domain if there is one, empty otherwise.
|
||||
*
|
||||
* <p>This attempts to load from the base {@link #labelsToKeys} if possible, otherwise it will
|
||||
* query the database for the entry requested.
|
||||
*/
|
||||
private Optional<String> getClaimKeyUncached(String label) {
|
||||
if (labelsToKeys != null) {
|
||||
return Optional.ofNullable(labelsToKeys.get(label));
|
||||
}
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.createQueryComposer(ClaimsEntry.class)
|
||||
.where("revisionId", EQ, revisionId)
|
||||
.where("domainLabel", EQ, label)
|
||||
.first()
|
||||
.map(ClaimsEntry::getClaimKey));
|
||||
}
|
||||
|
||||
public static ClaimsList create(
|
||||
DateTime tmdbGenerationTime, ImmutableMap<String, String> labelsToKeys) {
|
||||
ClaimsList instance = new ClaimsList();
|
||||
|
||||
@@ -14,25 +14,57 @@
|
||||
|
||||
package google.registry.model.tmch;
|
||||
|
||||
import static google.registry.config.RegistryConfig.getClaimsListCacheDuration;
|
||||
import static google.registry.persistence.transaction.QueryComposer.Comparator.EQ;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.CacheUtils;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Data access object for {@link ClaimsList}. */
|
||||
public class ClaimsListDao {
|
||||
|
||||
/**
|
||||
* Cache of the {@link ClaimsList} instance.
|
||||
*
|
||||
* <p>The key is meaningless since we only have one active claims list, this is essentially a
|
||||
* memoizing Supplier that can be reset.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static LoadingCache<Class<ClaimsListDao>, ClaimsList> CACHE =
|
||||
createCache(getClaimsListCacheDuration());
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setCacheForTest(Optional<Duration> expiry) {
|
||||
Duration effectiveExpiry = expiry.orElse(getClaimsListCacheDuration());
|
||||
CACHE = createCache(effectiveExpiry);
|
||||
}
|
||||
|
||||
private static LoadingCache<Class<ClaimsListDao>, ClaimsList> createCache(Duration expiry) {
|
||||
return CacheUtils.newCacheBuilder(expiry).build(ignored -> ClaimsListDao.getUncached());
|
||||
}
|
||||
|
||||
/** Saves the given {@link ClaimsList} to Cloud SQL. */
|
||||
public static void save(ClaimsList claimsList) {
|
||||
jpaTm().transact(() -> jpaTm().insert(claimsList));
|
||||
CACHE.put(ClaimsListDao.class, claimsList);
|
||||
}
|
||||
|
||||
/** Returns the most recent revision of the {@link ClaimsList} from the cache. */
|
||||
public static ClaimsList get() {
|
||||
return CACHE.get(ClaimsListDao.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most recent revision of the {@link ClaimsList} in SQL or an empty list if it
|
||||
* doesn't exist.
|
||||
*/
|
||||
public static ClaimsList get() {
|
||||
private static ClaimsList getUncached() {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
|
||||
@@ -37,7 +37,7 @@ import com.google.common.net.InetAddresses;
|
||||
import com.google.common.primitives.Booleans;
|
||||
import com.googlecode.objectify.cmd.Query;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
|
||||
@@ -301,8 +301,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
*/
|
||||
private DomainSearchResponse searchByNameserverLdhName(
|
||||
final RdapSearchPattern partialStringQuery) {
|
||||
ImmutableCollection<VKey<HostResource>> hostKeys =
|
||||
getNameserverRefsByLdhName(partialStringQuery);
|
||||
ImmutableCollection<VKey<Host>> hostKeys = getNameserverRefsByLdhName(partialStringQuery);
|
||||
if (Iterables.isEmpty(hostKeys)) {
|
||||
metricInformationBuilder.setNumHostsRetrieved(0);
|
||||
throw new NotFoundException("No matching nameservers found");
|
||||
@@ -311,7 +310,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles a list of {@link HostResource} keys by name.
|
||||
* Assembles a list of {@link Host} keys by name.
|
||||
*
|
||||
* <p>Nameserver query strings with wildcards are allowed to have a suffix after the wildcard,
|
||||
* which must be a domain. If the domain is not specified, or is not an existing domain in one of
|
||||
@@ -320,7 +319,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
* initial string is not required (e.g. "*.example.tld" is valid), because we can look up the
|
||||
* domain and just list all of its subordinate hosts.
|
||||
*/
|
||||
private ImmutableCollection<VKey<HostResource>> getNameserverRefsByLdhName(
|
||||
private ImmutableCollection<VKey<Host>> getNameserverRefsByLdhName(
|
||||
final RdapSearchPattern partialStringQuery) {
|
||||
// Handle queries without a wildcard.
|
||||
if (!partialStringQuery.getHasWildcard()) {
|
||||
@@ -341,9 +340,9 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
// limit in order to avoid arbitrarily long-running queries.
|
||||
Optional<String> desiredRegistrar = getDesiredRegistrar();
|
||||
if (tm().isOfy()) {
|
||||
Query<HostResource> query =
|
||||
Query<Host> query =
|
||||
queryItems(
|
||||
HostResource.class,
|
||||
Host.class,
|
||||
"fullyQualifiedHostName",
|
||||
partialStringQuery,
|
||||
Optional.empty(),
|
||||
@@ -359,9 +358,9 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
return replicaJpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
CriteriaQueryBuilder<HostResource> builder =
|
||||
CriteriaQueryBuilder<Host> builder =
|
||||
queryItemsSql(
|
||||
HostResource.class,
|
||||
Host.class,
|
||||
"fullyQualifiedHostName",
|
||||
partialStringQuery,
|
||||
Optional.empty(),
|
||||
@@ -376,22 +375,22 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
return getMatchingResourcesSql(builder, true, maxNameserversInFirstStage)
|
||||
.resources()
|
||||
.stream()
|
||||
.map(HostResource::createVKey)
|
||||
.map(Host::createVKey)
|
||||
.collect(toImmutableSet());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Assembles a list of {@link HostResource} keys by name when the pattern has no wildcard. */
|
||||
private ImmutableList<VKey<HostResource>> getNameserverRefsByLdhNameWithoutWildcard(
|
||||
/** Assembles a list of {@link Host} keys by name when the pattern has no wildcard. */
|
||||
private ImmutableList<VKey<Host>> getNameserverRefsByLdhNameWithoutWildcard(
|
||||
final RdapSearchPattern partialStringQuery) {
|
||||
// If we need to check the sponsoring registrar, we need to load the resource rather than just
|
||||
// the key.
|
||||
Optional<String> desiredRegistrar = getDesiredRegistrar();
|
||||
if (desiredRegistrar.isPresent()) {
|
||||
Optional<HostResource> host =
|
||||
Optional<Host> host =
|
||||
loadByForeignKey(
|
||||
HostResource.class,
|
||||
Host.class,
|
||||
partialStringQuery.getInitialString(),
|
||||
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
|
||||
return (!host.isPresent()
|
||||
@@ -399,17 +398,17 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
? ImmutableList.of()
|
||||
: ImmutableList.of(host.get().createVKey());
|
||||
} else {
|
||||
VKey<HostResource> hostKey =
|
||||
VKey<Host> hostKey =
|
||||
loadAndGetKey(
|
||||
HostResource.class,
|
||||
Host.class,
|
||||
partialStringQuery.getInitialString(),
|
||||
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
|
||||
return (hostKey == null) ? ImmutableList.of() : ImmutableList.of(hostKey);
|
||||
}
|
||||
}
|
||||
|
||||
/** Assembles a list of {@link HostResource} keys by name using a superordinate domain suffix. */
|
||||
private ImmutableList<VKey<HostResource>> getNameserverRefsByLdhNameWithSuffix(
|
||||
/** Assembles a list of {@link Host} keys by name using a superordinate domain suffix. */
|
||||
private ImmutableList<VKey<Host>> getNameserverRefsByLdhNameWithSuffix(
|
||||
final RdapSearchPattern partialStringQuery) {
|
||||
// The suffix must be a domain that we manage. That way, we can look up the domain and search
|
||||
// through the subordinate hosts. This is more efficient, and lets us permit wildcard searches
|
||||
@@ -425,17 +424,15 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
"A suffix in a lookup by nameserver name "
|
||||
+ "must be a domain defined in the system"));
|
||||
Optional<String> desiredRegistrar = getDesiredRegistrar();
|
||||
ImmutableList.Builder<VKey<HostResource>> builder = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<VKey<Host>> builder = new ImmutableList.Builder<>();
|
||||
for (String fqhn : ImmutableSortedSet.copyOf(domain.getSubordinateHosts())) {
|
||||
// We can't just check that the host name starts with the initial query string, because
|
||||
// then the query ns.exam*.example.com would match against nameserver ns.example.com.
|
||||
if (partialStringQuery.matches(fqhn)) {
|
||||
if (desiredRegistrar.isPresent()) {
|
||||
Optional<HostResource> host =
|
||||
Optional<Host> host =
|
||||
loadByForeignKey(
|
||||
HostResource.class,
|
||||
fqhn,
|
||||
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
|
||||
Host.class, fqhn, shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
|
||||
if (host.isPresent()
|
||||
&& desiredRegistrar
|
||||
.get()
|
||||
@@ -443,11 +440,9 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
builder.add(host.get().createVKey());
|
||||
}
|
||||
} else {
|
||||
VKey<HostResource> hostKey =
|
||||
VKey<Host> hostKey =
|
||||
loadAndGetKey(
|
||||
HostResource.class,
|
||||
fqhn,
|
||||
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
|
||||
Host.class, fqhn, shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
|
||||
if (hostKey != null) {
|
||||
builder.add(hostKey);
|
||||
} else {
|
||||
@@ -477,11 +472,11 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
*/
|
||||
private DomainSearchResponse searchByNameserverIp(final InetAddress inetAddress) {
|
||||
Optional<String> desiredRegistrar = getDesiredRegistrar();
|
||||
ImmutableSet<VKey<HostResource>> hostKeys;
|
||||
ImmutableSet<VKey<Host>> hostKeys;
|
||||
if (tm().isOfy()) {
|
||||
Query<HostResource> query =
|
||||
Query<Host> query =
|
||||
queryItems(
|
||||
HostResource.class,
|
||||
Host.class,
|
||||
"inetAddresses",
|
||||
inetAddress.getHostAddress(),
|
||||
Optional.empty(),
|
||||
@@ -524,7 +519,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
@SuppressWarnings("unchecked")
|
||||
Stream<String> resultStream = query.getResultStream();
|
||||
return resultStream
|
||||
.map(repoId -> VKey.create(HostResource.class, repoId))
|
||||
.map(repoId -> VKey.create(Host.class, repoId))
|
||||
.collect(toImmutableSet());
|
||||
});
|
||||
}
|
||||
@@ -538,7 +533,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
* #searchByNameserverIp} after they assemble the relevant host keys.
|
||||
*/
|
||||
private DomainSearchResponse searchByNameserverRefs(
|
||||
final ImmutableCollection<VKey<HostResource>> hostKeys) {
|
||||
final ImmutableCollection<VKey<Host>> hostKeys) {
|
||||
// We must break the query up into chunks, because the in operator is limited to 30 subqueries.
|
||||
// Since it is possible for the same domain to show up more than once in our result list (if
|
||||
// we do a wildcard nameserver search that returns multiple nameservers used by the same
|
||||
@@ -548,7 +543,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
ImmutableSortedSet.Builder<Domain> domainSetBuilder =
|
||||
ImmutableSortedSet.orderedBy(Comparator.comparing(Domain::getDomainName));
|
||||
int numHostKeysSearched = 0;
|
||||
for (List<VKey<HostResource>> chunk : Iterables.partition(hostKeys, 30)) {
|
||||
for (List<VKey<Host>> chunk : Iterables.partition(hostKeys, 30)) {
|
||||
numHostKeysSearched += chunk.size();
|
||||
if (tm().isOfy()) {
|
||||
Query<Domain> query =
|
||||
@@ -574,7 +569,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
replicaJpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
for (VKey<HostResource> hostKey : hostKeys) {
|
||||
for (VKey<Host> hostKey : hostKeys) {
|
||||
CriteriaQueryBuilder<Domain> queryBuilder =
|
||||
CriteriaQueryBuilder.create(replicaJpaTm(), Domain.class)
|
||||
.whereFieldContains("nsHosts", hostKey)
|
||||
|
||||
@@ -48,7 +48,7 @@ import google.registry.model.domain.DesignatedContact.Type;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.eppcommon.Address;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
@@ -235,8 +235,8 @@ public class RdapJsonFormatter {
|
||||
ImmutableMap.of("type", ImmutableList.of("fax"));
|
||||
|
||||
/** Sets the ordering for hosts; just use the fully qualified host name. */
|
||||
private static final Ordering<HostResource> HOST_RESOURCE_ORDERING =
|
||||
Ordering.natural().onResultOf(HostResource::getHostName);
|
||||
private static final Ordering<Host> HOST_RESOURCE_ORDERING =
|
||||
Ordering.natural().onResultOf(Host::getHostName);
|
||||
|
||||
/** Sets the ordering for designated contacts; order them in a fixed order by contact type. */
|
||||
private static final Ordering<DesignatedContact> DESIGNATED_CONTACT_ORDERING =
|
||||
@@ -356,7 +356,7 @@ public class RdapJsonFormatter {
|
||||
|
||||
// Kick off the database loads of the nameservers that we will need, so it can load
|
||||
// asynchronously while we load and process the contacts.
|
||||
ImmutableSet<HostResource> loadedHosts =
|
||||
ImmutableSet<Host> loadedHosts =
|
||||
tm().transact(() -> ImmutableSet.copyOf(tm().loadByKeys(domain.getNameservers()).values()));
|
||||
// Load the registrant and other contacts and add them to the data.
|
||||
ImmutableMap<VKey<? extends ContactResource>, ContactResource> loadedContacts =
|
||||
@@ -393,8 +393,8 @@ public class RdapJsonFormatter {
|
||||
}
|
||||
// Add the nameservers to the data; the load was kicked off above for efficiency.
|
||||
// RDAP Response Profile 2.9: we MUST have the nameservers
|
||||
for (HostResource hostResource : HOST_RESOURCE_ORDERING.immutableSortedCopy(loadedHosts)) {
|
||||
builder.nameserversBuilder().add(createRdapNameserver(hostResource, OutputDataType.INTERNAL));
|
||||
for (Host host : HOST_RESOURCE_ORDERING.immutableSortedCopy(loadedHosts)) {
|
||||
builder.nameserversBuilder().add(createRdapNameserver(host, OutputDataType.INTERNAL));
|
||||
}
|
||||
|
||||
// RDAP Response Profile 2.10 - MUST contain a secureDns member including at least a
|
||||
@@ -410,23 +410,23 @@ public class RdapJsonFormatter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON object for a {@link HostResource}.
|
||||
* Creates a JSON object for a {@link Host}.
|
||||
*
|
||||
* @param hostResource the host resource object from which the JSON object should be created
|
||||
* @param host the host resource object from which the JSON object should be created
|
||||
* @param outputDataType whether to generate full or summary data
|
||||
*/
|
||||
RdapNameserver createRdapNameserver(HostResource hostResource, OutputDataType outputDataType) {
|
||||
RdapNameserver createRdapNameserver(Host host, OutputDataType outputDataType) {
|
||||
RdapNameserver.Builder builder = RdapNameserver.builder();
|
||||
builder.linksBuilder().add(makeSelfLink("nameserver", hostResource.getHostName()));
|
||||
builder.linksBuilder().add(makeSelfLink("nameserver", host.getHostName()));
|
||||
if (outputDataType != OutputDataType.FULL) {
|
||||
builder.remarksBuilder().add(RdapIcannStandardInformation.SUMMARY_DATA_REMARK);
|
||||
}
|
||||
|
||||
// We need the ldhName: RDAP Response Profile 2.9.1, 4.1
|
||||
builder.setLdhName(hostResource.getHostName());
|
||||
builder.setLdhName(host.getHostName());
|
||||
// Handle is optional, but if given it MUST be the ROID.
|
||||
// We will set it always as it's important as a "self link"
|
||||
builder.setHandle(hostResource.getRepoId());
|
||||
builder.setHandle(host.getRepoId());
|
||||
|
||||
// Status is optional for internal Nameservers - RDAP Response Profile 2.9.2
|
||||
// It isn't mentioned at all anywhere else. So we can just not put it at all?
|
||||
@@ -434,14 +434,14 @@ public class RdapJsonFormatter {
|
||||
// To be safe, we'll put it on the "FULL" version anyway
|
||||
if (outputDataType == OutputDataType.FULL) {
|
||||
ImmutableSet.Builder<StatusValue> statuses = new ImmutableSet.Builder<>();
|
||||
statuses.addAll(hostResource.getStatusValues());
|
||||
if (isLinked(hostResource.createVKey(), getRequestTime())) {
|
||||
statuses.addAll(host.getStatusValues());
|
||||
if (isLinked(host.createVKey(), getRequestTime())) {
|
||||
statuses.add(StatusValue.LINKED);
|
||||
}
|
||||
if (hostResource.isSubordinate()
|
||||
if (host.isSubordinate()
|
||||
&& tm().transact(
|
||||
() ->
|
||||
tm().loadByKey(hostResource.getSuperordinateDomain())
|
||||
tm().loadByKey(host.getSuperordinateDomain())
|
||||
.cloneProjectedAtTime(getRequestTime())
|
||||
.getStatusValues()
|
||||
.contains(StatusValue.PENDING_TRANSFER))) {
|
||||
@@ -453,14 +453,14 @@ public class RdapJsonFormatter {
|
||||
makeStatusValueList(
|
||||
statuses.build(),
|
||||
false, // isRedacted
|
||||
hostResource.getDeletionTime().isBefore(getRequestTime())));
|
||||
host.getDeletionTime().isBefore(getRequestTime())));
|
||||
}
|
||||
|
||||
// For query responses - we MUST have all the ip addresses: RDAP Response Profile 4.2.
|
||||
//
|
||||
// However, it is optional for internal responses: RDAP Response Profile 2.9.2
|
||||
if (outputDataType != OutputDataType.INTERNAL) {
|
||||
for (InetAddress inetAddress : hostResource.getInetAddresses()) {
|
||||
for (InetAddress inetAddress : host.getInetAddresses()) {
|
||||
if (inetAddress instanceof Inet4Address) {
|
||||
builder.ipv4Builder().add(InetAddresses.toAddrString(inetAddress));
|
||||
} else if (inetAddress instanceof Inet6Address) {
|
||||
@@ -472,8 +472,7 @@ public class RdapJsonFormatter {
|
||||
// RDAP Response Profile 4.3 - Registrar member is optional, so we only set it for FULL
|
||||
if (outputDataType == OutputDataType.FULL) {
|
||||
Registrar registrar =
|
||||
Registrar.loadRequiredRegistrarCached(
|
||||
hostResource.getPersistedCurrentSponsorRegistrarId());
|
||||
Registrar.loadRequiredRegistrarCached(host.getPersistedCurrentSponsorRegistrarId());
|
||||
builder.entitiesBuilder().add(createRdapRegistrarEntity(registrar, OutputDataType.INTERNAL));
|
||||
}
|
||||
if (outputDataType != OutputDataType.INTERNAL) {
|
||||
|
||||
@@ -21,7 +21,7 @@ import static google.registry.request.Action.Method.HEAD;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
|
||||
import google.registry.rdap.RdapMetrics.EndpointType;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapNameserver;
|
||||
@@ -61,12 +61,12 @@ public class RdapNameserverAction extends RdapActionBase {
|
||||
}
|
||||
// If there are no undeleted nameservers with the given name, the foreign key should point to
|
||||
// the most recently deleted one.
|
||||
Optional<HostResource> hostResource =
|
||||
Optional<Host> host =
|
||||
loadByForeignKey(
|
||||
HostResource.class,
|
||||
Host.class,
|
||||
pathSearchString,
|
||||
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
|
||||
if (!hostResource.isPresent() || !isAuthorized(hostResource.get())) {
|
||||
if (!host.isPresent() || !isAuthorized(host.get())) {
|
||||
// RFC7480 5.3 - if the server wishes to respond that it doesn't have data satisfying the
|
||||
// query, it MUST reply with 404 response code.
|
||||
//
|
||||
@@ -74,6 +74,6 @@ public class RdapNameserverAction extends RdapActionBase {
|
||||
// exists but we don't want to show it to you", because we DON'T wish to say that.
|
||||
throw new NotFoundException(pathSearchString + " not found");
|
||||
}
|
||||
return rdapJsonFormatter.createRdapNameserver(hostResource.get(), OutputDataType.FULL);
|
||||
return rdapJsonFormatter.createRdapNameserver(host.get(), OutputDataType.FULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import com.google.common.net.InetAddresses;
|
||||
import com.google.common.primitives.Booleans;
|
||||
import com.googlecode.objectify.cmd.Query;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
|
||||
import google.registry.rdap.RdapMetrics.EndpointType;
|
||||
@@ -159,16 +159,15 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
NameserverSearchResponse.builder()
|
||||
.setIncompletenessWarningType(IncompletenessWarningType.COMPLETE);
|
||||
|
||||
Optional<HostResource> hostResource =
|
||||
loadByForeignKey(
|
||||
HostResource.class, partialStringQuery.getInitialString(), getRequestTime());
|
||||
Optional<Host> host =
|
||||
loadByForeignKey(Host.class, partialStringQuery.getInitialString(), getRequestTime());
|
||||
|
||||
metricInformationBuilder.setNumHostsRetrieved(hostResource.isPresent() ? 1 : 0);
|
||||
metricInformationBuilder.setNumHostsRetrieved(host.isPresent() ? 1 : 0);
|
||||
|
||||
if (shouldBeVisible(hostResource)) {
|
||||
if (shouldBeVisible(host)) {
|
||||
builder
|
||||
.nameserverSearchResultsBuilder()
|
||||
.add(rdapJsonFormatter.createRdapNameserver(hostResource.get(), OutputDataType.FULL));
|
||||
.add(rdapJsonFormatter.createRdapNameserver(host.get(), OutputDataType.FULL));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
@@ -187,7 +186,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
throw new UnprocessableEntityException(
|
||||
"A suffix after a wildcard in a nameserver lookup must be an in-bailiwick domain");
|
||||
}
|
||||
List<HostResource> hostList = new ArrayList<>();
|
||||
List<Host> hostList = new ArrayList<>();
|
||||
for (String fqhn : ImmutableSortedSet.copyOf(domain.get().getSubordinateHosts())) {
|
||||
if (cursorString.isPresent() && (fqhn.compareTo(cursorString.get()) <= 0)) {
|
||||
continue;
|
||||
@@ -195,10 +194,9 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
// We can't just check that the host name starts with the initial query string, because
|
||||
// then the query ns.exam*.example.com would match against nameserver ns.example.com.
|
||||
if (partialStringQuery.matches(fqhn)) {
|
||||
Optional<HostResource> hostResource =
|
||||
loadByForeignKey(HostResource.class, fqhn, getRequestTime());
|
||||
if (shouldBeVisible(hostResource)) {
|
||||
hostList.add(hostResource.get());
|
||||
Optional<Host> host = loadByForeignKey(Host.class, fqhn, getRequestTime());
|
||||
if (shouldBeVisible(host)) {
|
||||
hostList.add(host.get());
|
||||
if (hostList.size() > rdapResultSetMaxSize) {
|
||||
break;
|
||||
}
|
||||
@@ -222,9 +220,9 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
// Add 1 so we can detect truncation.
|
||||
int querySizeLimit = getStandardQuerySizeLimit();
|
||||
if (tm().isOfy()) {
|
||||
Query<HostResource> query =
|
||||
Query<Host> query =
|
||||
queryItems(
|
||||
HostResource.class,
|
||||
Host.class,
|
||||
"fullyQualifiedHostName",
|
||||
partialStringQuery,
|
||||
cursorString,
|
||||
@@ -236,9 +234,9 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
return replicaJpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
CriteriaQueryBuilder<HostResource> queryBuilder =
|
||||
CriteriaQueryBuilder<Host> queryBuilder =
|
||||
queryItemsSql(
|
||||
HostResource.class,
|
||||
Host.class,
|
||||
"fullyQualifiedHostName",
|
||||
partialStringQuery,
|
||||
cursorString,
|
||||
@@ -254,11 +252,11 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
private NameserverSearchResponse searchByIp(InetAddress inetAddress) {
|
||||
// Add 1 so we can detect truncation.
|
||||
int querySizeLimit = getStandardQuerySizeLimit();
|
||||
RdapResultSet<HostResource> rdapResultSet;
|
||||
RdapResultSet<Host> rdapResultSet;
|
||||
if (tm().isOfy()) {
|
||||
Query<HostResource> query =
|
||||
Query<Host> query =
|
||||
queryItems(
|
||||
HostResource.class,
|
||||
Host.class,
|
||||
"inetAddresses",
|
||||
inetAddress.getHostAddress(),
|
||||
Optional.empty(),
|
||||
@@ -296,11 +294,11 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
javax.persistence.Query query =
|
||||
replicaJpaTm()
|
||||
.getEntityManager()
|
||||
.createNativeQuery(queryBuilder.toString(), HostResource.class)
|
||||
.createNativeQuery(queryBuilder.toString(), Host.class)
|
||||
.setMaxResults(querySizeLimit);
|
||||
parameters.build().forEach(query::setParameter);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<HostResource> resultList = query.getResultList();
|
||||
List<Host> resultList = query.getResultList();
|
||||
return filterResourcesByVisibility(resultList, querySizeLimit);
|
||||
});
|
||||
}
|
||||
@@ -309,7 +307,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
|
||||
/** Output JSON for a lists of hosts contained in an {@link RdapResultSet}. */
|
||||
private NameserverSearchResponse makeSearchResults(
|
||||
RdapResultSet<HostResource> resultSet, CursorType cursorType) {
|
||||
RdapResultSet<Host> resultSet, CursorType cursorType) {
|
||||
return makeSearchResults(
|
||||
resultSet.resources(),
|
||||
resultSet.incompletenessWarningType(),
|
||||
@@ -319,7 +317,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
|
||||
/** Output JSON for a list of hosts. */
|
||||
private NameserverSearchResponse makeSearchResults(
|
||||
List<HostResource> hosts,
|
||||
List<Host> hosts,
|
||||
IncompletenessWarningType incompletenessWarningType,
|
||||
int numHostsRetrieved,
|
||||
CursorType cursorType) {
|
||||
@@ -329,7 +327,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
NameserverSearchResponse.Builder builder =
|
||||
NameserverSearchResponse.builder().setIncompletenessWarningType(incompletenessWarningType);
|
||||
Optional<String> newCursor = Optional.empty();
|
||||
for (HostResource host : Iterables.limit(hosts, rdapResultSetMaxSize)) {
|
||||
for (Host host : Iterables.limit(hosts, rdapResultSetMaxSize)) {
|
||||
newCursor =
|
||||
Optional.of((cursorType == CursorType.NAME) ? host.getHostName() : host.getRepoId());
|
||||
builder
|
||||
|
||||
+13
-13
@@ -19,7 +19,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import com.google.common.net.InetAddresses;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.xjc.host.XjcHostAddrType;
|
||||
import google.registry.xjc.host.XjcHostIpType;
|
||||
import google.registry.xjc.host.XjcHostStatusType;
|
||||
@@ -30,23 +30,23 @@ import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Utility class that turns {@link HostResource} as {@link XjcRdeHostElement}. */
|
||||
final class HostResourceToXjcConverter {
|
||||
/** Utility class that turns a {@link Host} resource into {@link XjcRdeHostElement}. */
|
||||
final class HostToXjcConverter {
|
||||
|
||||
/** Converts a subordinate {@link HostResource} to {@link XjcRdeHostElement}. */
|
||||
static XjcRdeHostElement convertSubordinate(HostResource host, Domain superordinateDomain) {
|
||||
/** Converts a subordinate {@link Host} to {@link XjcRdeHostElement}. */
|
||||
static XjcRdeHostElement convertSubordinate(Host host, Domain superordinateDomain) {
|
||||
checkArgument(superordinateDomain.createVKey().equals(host.getSuperordinateDomain()));
|
||||
return new XjcRdeHostElement(convertSubordinateHost(host, superordinateDomain));
|
||||
}
|
||||
|
||||
/** Converts an external {@link HostResource} to {@link XjcRdeHostElement}. */
|
||||
static XjcRdeHostElement convertExternal(HostResource host) {
|
||||
/** Converts an external {@link Host} to {@link XjcRdeHostElement}. */
|
||||
static XjcRdeHostElement convertExternal(Host host) {
|
||||
checkArgument(!host.isSubordinate());
|
||||
return new XjcRdeHostElement(convertExternalHost(host));
|
||||
}
|
||||
|
||||
/** Converts {@link HostResource} to {@link XjcRdeHost}. */
|
||||
static XjcRdeHost convertSubordinateHost(HostResource model, Domain superordinateDomain) {
|
||||
/** Converts {@link Host} to {@link XjcRdeHost}. */
|
||||
static XjcRdeHost convertSubordinateHost(Host model, Domain superordinateDomain) {
|
||||
XjcRdeHost bean =
|
||||
convertHostCommon(
|
||||
model,
|
||||
@@ -58,14 +58,14 @@ final class HostResourceToXjcConverter {
|
||||
return bean;
|
||||
}
|
||||
|
||||
/** Converts {@link HostResource} to {@link XjcRdeHost}. */
|
||||
static XjcRdeHost convertExternalHost(HostResource model) {
|
||||
/** Converts {@link Host} to {@link XjcRdeHost}. */
|
||||
static XjcRdeHost convertExternalHost(Host model) {
|
||||
return convertHostCommon(
|
||||
model, model.getPersistedCurrentSponsorRegistrarId(), model.getLastTransferTime());
|
||||
}
|
||||
|
||||
private static XjcRdeHost convertHostCommon(
|
||||
HostResource model, String registrarId, DateTime lastTransferTime) {
|
||||
Host model, String registrarId, DateTime lastTransferTime) {
|
||||
XjcRdeHost bean = new XjcRdeHost();
|
||||
bean.setName(model.getHostName());
|
||||
bean.setRoid(model.getRepoId());
|
||||
@@ -105,5 +105,5 @@ final class HostResourceToXjcConverter {
|
||||
return bean;
|
||||
}
|
||||
|
||||
private HostResourceToXjcConverter() {}
|
||||
private HostToXjcConverter() {}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.rde.RdeMode;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -71,8 +71,8 @@ public class RdeFragmenter {
|
||||
cache.put(WatermarkModePair.create(watermark, RdeMode.FULL), result);
|
||||
cache.put(WatermarkModePair.create(watermark, RdeMode.THIN), result);
|
||||
return result;
|
||||
} else if (resource instanceof HostResource) {
|
||||
HostResource host = (HostResource) resource;
|
||||
} else if (resource instanceof Host) {
|
||||
Host host = (Host) resource;
|
||||
result =
|
||||
Optional.of(
|
||||
host.isSubordinate()
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.googlecode.objectify.Key;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.rde.RdeMode;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.tldconfig.idn.IdnTable;
|
||||
@@ -129,16 +129,17 @@ public final class RdeMarshaller implements Serializable {
|
||||
RdeResourceType.DOMAIN, domain, DomainToXjcConverter.convert(domain, mode));
|
||||
}
|
||||
|
||||
/** Turns {@link HostResource} object into an XML fragment. */
|
||||
public DepositFragment marshalSubordinateHost(HostResource host, Domain superordinateDomain) {
|
||||
return marshalResource(RdeResourceType.HOST, host,
|
||||
HostResourceToXjcConverter.convertSubordinate(host, superordinateDomain));
|
||||
/** Turns {@link Host} object into an XML fragment. */
|
||||
public DepositFragment marshalSubordinateHost(Host host, Domain superordinateDomain) {
|
||||
return marshalResource(
|
||||
RdeResourceType.HOST,
|
||||
host,
|
||||
HostToXjcConverter.convertSubordinate(host, superordinateDomain));
|
||||
}
|
||||
|
||||
/** Turns {@link HostResource} object into an XML fragment. */
|
||||
public DepositFragment marshalExternalHost(HostResource host) {
|
||||
return marshalResource(RdeResourceType.HOST, host,
|
||||
HostResourceToXjcConverter.convertExternal(host));
|
||||
/** Turns {@link Host} object into an XML fragment. */
|
||||
public DepositFragment marshalExternalHost(Host host) {
|
||||
return marshalResource(RdeResourceType.HOST, host, HostToXjcConverter.convertExternal(host));
|
||||
}
|
||||
|
||||
/** Turns {@link Registrar} object into an XML fragment. */
|
||||
|
||||
@@ -47,7 +47,7 @@ import google.registry.model.common.Cursor;
|
||||
import google.registry.model.common.Cursor.CursorType;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.index.EppResourceIndex;
|
||||
import google.registry.model.rde.RdeMode;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
@@ -82,8 +82,8 @@ import org.joda.time.Duration;
|
||||
* type and loads the embedded resource from it, which is then projected to watermark time to
|
||||
* account for things like pending transfer.
|
||||
*
|
||||
* <p>Only {@link ContactResource}s and {@link HostResource}s that are referenced by an included
|
||||
* {@link Domain} will be included in the corresponding pending deposit.
|
||||
* <p>Only {@link ContactResource}s and {@link Host}s that are referenced by an included {@link
|
||||
* Domain} will be included in the corresponding pending deposit.
|
||||
*
|
||||
* <p>{@link Registrar} entities, both active and inactive, are included in all deposits. They are
|
||||
* not rewinded point-in-time.
|
||||
|
||||
@@ -21,7 +21,7 @@ import com.google.common.base.Strings;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.persistence.VKey;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -32,7 +32,7 @@ class CommandUtilities {
|
||||
/** A useful parameter enum for commands that operate on {@link EppResource} objects. */
|
||||
public enum ResourceType {
|
||||
CONTACT(ContactResource.class),
|
||||
HOST(HostResource.class),
|
||||
HOST(Host.class),
|
||||
DOMAIN(Domain.class);
|
||||
|
||||
private final Class<? extends EppResource> clazz;
|
||||
|
||||
@@ -26,7 +26,7 @@ import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.transaction.QueryComposer.Comparator;
|
||||
import google.registry.tools.params.PathParameter;
|
||||
import google.registry.util.Clock;
|
||||
@@ -87,8 +87,8 @@ final class GenerateDnsReportCommand implements CommandWithRemoteApi {
|
||||
write(domain);
|
||||
}
|
||||
|
||||
Iterable<HostResource> nameservers = tm().transact(() -> tm().loadAllOf(HostResource.class));
|
||||
for (HostResource nameserver : nameservers) {
|
||||
Iterable<Host> nameservers = tm().transact(() -> tm().loadAllOf(Host.class));
|
||||
for (Host nameserver : nameservers) {
|
||||
// Skip deleted hosts and external hosts.
|
||||
if (isBeforeOrAt(nameserver.getDeletionTime(), now)
|
||||
|| nameserver.getInetAddresses().isEmpty()) {
|
||||
@@ -126,7 +126,7 @@ final class GenerateDnsReportCommand implements CommandWithRemoteApi {
|
||||
writeJson(mapBuilder.build());
|
||||
}
|
||||
|
||||
private void write(HostResource nameserver) {
|
||||
private void write(Host nameserver) {
|
||||
ImmutableList<String> ipAddresses =
|
||||
nameserver
|
||||
.getInetAddresses()
|
||||
|
||||
@@ -30,8 +30,8 @@ import java.nio.file.Paths;
|
||||
/**
|
||||
* A command to download the current claims list.
|
||||
*
|
||||
* <p>This is not the original file we fetched from TMCH. It is just a representation of what we
|
||||
* are currently storing in Datastore.
|
||||
* <p>This is not the original file we fetched from TMCH. It is just a representation of what we are
|
||||
* currently storing in SQL.
|
||||
*/
|
||||
@Parameters(separators = " =", commandDescription = "Download the current claims list")
|
||||
final class GetClaimsListCommand implements CommandWithRemoteApi {
|
||||
|
||||
@@ -18,7 +18,7 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import java.util.List;
|
||||
|
||||
@@ -35,7 +35,6 @@ final class GetHostCommand extends GetEppResourceCommand {
|
||||
public void runAndPrint() {
|
||||
mainParameters.stream()
|
||||
.map(DomainNameUtils::canonicalizeHostname)
|
||||
.forEach(
|
||||
h -> printResource("Host", h, loadByForeignKey(HostResource.class, h, readTimestamp)));
|
||||
.forEach(h -> printResource("Host", h, loadByForeignKey(Host.class, h, readTimestamp)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ package google.registry.tools;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.tools.server.ListHostsAction;
|
||||
|
||||
/** Command to list all HostResource entities in the system. */
|
||||
/** Command to list all Host entities in the system. */
|
||||
@Parameters(separators = " =", commandDescription = "List all hosts.")
|
||||
final class ListHostsCommand extends ListObjectsCommand {
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import com.google.template.soy.data.SoyMapData;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.tools.soy.DomainRenewSoyInfo;
|
||||
import google.registry.tools.soy.UniformRapidSuspensionSoyInfo;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
@@ -130,7 +130,7 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
|
||||
checkArgumentPresent(domainOpt, "Domain '%s' does not exist or is deleted", domainName);
|
||||
Domain domain = domainOpt.get();
|
||||
Set<String> missingHosts =
|
||||
difference(newHostsSet, checkResourcesExist(HostResource.class, newCanonicalHosts, now));
|
||||
difference(newHostsSet, checkResourcesExist(Host.class, newCanonicalHosts, now));
|
||||
checkArgument(missingHosts.isEmpty(), "Hosts do not exist: %s", missingHosts);
|
||||
checkArgument(
|
||||
locksToPreserve.isEmpty() || undo,
|
||||
@@ -204,8 +204,7 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
|
||||
|
||||
private ImmutableSortedSet<String> getExistingNameservers(Domain domain) {
|
||||
ImmutableSortedSet.Builder<String> nameservers = ImmutableSortedSet.naturalOrder();
|
||||
for (HostResource host :
|
||||
tm().transact(() -> tm().loadByKeys(domain.getNameservers()).values())) {
|
||||
for (Host host : tm().transact(() -> tm().loadByKeys(domain.getNameservers()).values())) {
|
||||
nameservers.add(host.getForeignKey());
|
||||
}
|
||||
return nameservers.build();
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.Period;
|
||||
@@ -53,7 +54,7 @@ import org.joda.time.DateTime;
|
||||
* <p>This removes years off a domain's registration period. Note that the expiration time cannot be
|
||||
* set to prior than the present. Reversal of the charges for these years (if desired) must happen
|
||||
* out of band, as they may already have been billed out and thus cannot and won't be reversed in
|
||||
* Datastore.
|
||||
* the database.
|
||||
*/
|
||||
@Parameters(separators = " =", commandDescription = "Unrenew a domain.")
|
||||
@NonFinalForTesting
|
||||
@@ -215,7 +216,9 @@ class UnrenewDomainCommand extends ConfirmingCommand implements CommandWithRemot
|
||||
.setHistoryEntry(domainHistory)
|
||||
.build();
|
||||
// End the old autorenew billing event and poll message now.
|
||||
updateAutorenewRecurrenceEndTime(domain, now);
|
||||
Recurring existingRecurring = tm().loadByKey(domain.getAutorenewBillingEvent());
|
||||
updateAutorenewRecurrenceEndTime(
|
||||
domain, existingRecurring, now, domainHistory.getDomainHistoryId());
|
||||
Domain newDomain =
|
||||
domain
|
||||
.asBuilder()
|
||||
@@ -223,9 +226,7 @@ class UnrenewDomainCommand extends ConfirmingCommand implements CommandWithRemot
|
||||
.setLastEppUpdateTime(now)
|
||||
.setLastEppUpdateRegistrarId(domain.getCurrentSponsorRegistrarId())
|
||||
.setAutorenewBillingEvent(newAutorenewEvent.createVKey())
|
||||
.setAutorenewPollMessage(
|
||||
newAutorenewPollMessage.createVKey(),
|
||||
newAutorenewPollMessage.getHistoryRevisionId())
|
||||
.setAutorenewPollMessage(newAutorenewPollMessage.createVKey())
|
||||
.build();
|
||||
// In order to do it'll need to write out a new HistoryEntry (likely of type SYNTHETIC), a new
|
||||
// autorenew billing event and poll message, and a new one time poll message at the present time
|
||||
|
||||
@@ -17,13 +17,13 @@ package google.registry.tools.params;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
|
||||
/** Enum to make it easy for a command to accept a flag that specifies an EppResource subclass. */
|
||||
public enum EppResourceTypeParameter {
|
||||
CONTACT(ContactResource.class),
|
||||
DOMAIN(Domain.class),
|
||||
HOST(HostResource.class);
|
||||
HOST(Host.class);
|
||||
|
||||
private final Class<? extends EppResource> type;
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.gcs.GcsUtils;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.request.JsonActionRunner;
|
||||
@@ -196,8 +196,8 @@ public class GenerateZoneFilesAction implements Runnable, JsonActionRunner.JsonA
|
||||
Domain domain, DateTime exportTime, ImmutableList.Builder<String> result) {
|
||||
ImmutableSet<String> subordinateHosts = domain.getSubordinateHosts();
|
||||
if (!subordinateHosts.isEmpty()) {
|
||||
for (HostResource unprojectedHost : tm().loadByKeys(domain.getNameservers()).values()) {
|
||||
HostResource host = loadAtPointInTime(unprojectedHost, exportTime);
|
||||
for (Host unprojectedHost : tm().loadByKeys(domain.getNameservers()).values()) {
|
||||
Host host = loadAtPointInTime(unprojectedHost, exportTime);
|
||||
// A null means the host was deleted (or not created) at this time.
|
||||
if (host != null && subordinateHosts.contains(host.getHostName())) {
|
||||
String stanza = hostStanza(host, domain.getTld());
|
||||
@@ -232,7 +232,7 @@ public class GenerateZoneFilesAction implements Runnable, JsonActionRunner.JsonA
|
||||
private String domainStanza(Domain domain, DateTime exportTime) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
String domainLabel = stripTld(domain.getDomainName(), domain.getTld());
|
||||
for (HostResource nameserver : tm().loadByKeys(domain.getNameservers()).values()) {
|
||||
for (Host nameserver : tm().loadByKeys(domain.getNameservers()).values()) {
|
||||
result.append(
|
||||
String.format(
|
||||
NS_FORMAT,
|
||||
@@ -267,7 +267,7 @@ public class GenerateZoneFilesAction implements Runnable, JsonActionRunner.JsonA
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
private String hostStanza(HostResource host, String tld) {
|
||||
private String hostStanza(Host host, String tld) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (InetAddress addr : host.getInetAddresses()) {
|
||||
// must be either IPv4 or IPv6
|
||||
|
||||
@@ -22,7 +22,7 @@ import static java.util.Comparator.comparing;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.EppResourceUtils;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Clock;
|
||||
@@ -35,7 +35,7 @@ import org.joda.time.DateTime;
|
||||
path = ListHostsAction.PATH,
|
||||
method = {GET, POST},
|
||||
auth = Auth.AUTH_INTERNAL_OR_ADMIN)
|
||||
public final class ListHostsAction extends ListObjectsAction<HostResource> {
|
||||
public final class ListHostsAction extends ListObjectsAction<Host> {
|
||||
|
||||
public static final String PATH = "/_dr/admin/list/hosts";
|
||||
|
||||
@@ -48,10 +48,10 @@ public final class ListHostsAction extends ListObjectsAction<HostResource> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<HostResource> loadObjects() {
|
||||
public ImmutableSet<Host> loadObjects() {
|
||||
final DateTime now = clock.nowUtc();
|
||||
return tm().transact(() -> tm().loadAllOf(HostResource.class)).stream()
|
||||
return tm().transact(() -> tm().loadAllOf(Host.class)).stream()
|
||||
.filter(host -> EppResourceUtils.isActive(host, now))
|
||||
.collect(toImmutableSortedSet(comparing(HostResource::getHostName)));
|
||||
.collect(toImmutableSortedSet(comparing(Host::getHostName)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyCached;
|
||||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -34,10 +34,10 @@ public class NameserverLookupByHostCommand extends DomainOrHostLookupCommand {
|
||||
|
||||
@Override
|
||||
protected Optional<WhoisResponse> getResponse(InternetDomainName hostName, DateTime now) {
|
||||
Optional<HostResource> hostResource =
|
||||
Optional<Host> host =
|
||||
cached
|
||||
? loadByForeignKeyCached(HostResource.class, hostName.toString(), now)
|
||||
: loadByForeignKey(HostResource.class, hostName.toString(), now);
|
||||
return hostResource.map(host -> new NameserverWhoisResponse(host, now));
|
||||
? loadByForeignKeyCached(Host.class, hostName.toString(), now)
|
||||
: loadByForeignKey(Host.class, hostName.toString(), now);
|
||||
return host.map(h -> new NameserverWhoisResponse(h, now));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.net.InetAddresses;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.tld.Registries;
|
||||
import java.net.InetAddress;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -51,12 +51,12 @@ final class NameserverLookupByIpCommand implements WhoisCommand {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public WhoisResponse executeQuery(DateTime now) throws WhoisException {
|
||||
Iterable<HostResource> hostsFromDb;
|
||||
Iterable<Host> hostsFromDb;
|
||||
if (tm().isOfy()) {
|
||||
hostsFromDb =
|
||||
auditedOfy()
|
||||
.load()
|
||||
.type(HostResource.class)
|
||||
.type(Host.class)
|
||||
.filter("inetAddresses", ipAddress)
|
||||
.filter("deletionTime >", now.toDate());
|
||||
} else {
|
||||
@@ -76,12 +76,12 @@ final class NameserverLookupByIpCommand implements WhoisCommand {
|
||||
"SELECT * From \"Host\" WHERE "
|
||||
+ "ARRAY[ CAST(:address AS TEXT) ] <@ inet_addresses AND "
|
||||
+ "deletion_time > CAST(:now AS timestamptz)",
|
||||
HostResource.class)
|
||||
Host.class)
|
||||
.setParameter("address", InetAddresses.toAddrString(ipAddress))
|
||||
.setParameter("now", now.toString())
|
||||
.getResultList());
|
||||
}
|
||||
ImmutableList<HostResource> hosts =
|
||||
ImmutableList<Host> hosts =
|
||||
Streams.stream(hostsFromDb)
|
||||
.filter(
|
||||
host ->
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.net.InetAddresses;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -32,15 +32,15 @@ import org.joda.time.DateTime;
|
||||
final class NameserverWhoisResponse extends WhoisResponseImpl {
|
||||
|
||||
/** Nameserver(s) which were the target of this WHOIS command. */
|
||||
private final ImmutableList<HostResource> hosts;
|
||||
private final ImmutableList<Host> hosts;
|
||||
|
||||
/** Creates new WHOIS nameserver response on the given host. */
|
||||
NameserverWhoisResponse(HostResource host, DateTime timestamp) {
|
||||
NameserverWhoisResponse(Host host, DateTime timestamp) {
|
||||
this(ImmutableList.of(checkNotNull(host, "host")), timestamp);
|
||||
}
|
||||
|
||||
/** Creates new WHOIS nameserver response on the given list of hosts. */
|
||||
NameserverWhoisResponse(ImmutableList<HostResource> hosts, DateTime timestamp) {
|
||||
NameserverWhoisResponse(ImmutableList<Host> hosts, DateTime timestamp) {
|
||||
super(timestamp);
|
||||
this.hosts = checkNotNull(hosts, "hosts");
|
||||
}
|
||||
@@ -48,9 +48,9 @@ final class NameserverWhoisResponse extends WhoisResponseImpl {
|
||||
@Override
|
||||
public WhoisResponseResults getResponse(boolean preferUnicode, String disclaimer) {
|
||||
// If we have subordinate hosts, load their registrar ids in a single transaction up-front.
|
||||
ImmutableList<HostResource> subordinateHosts =
|
||||
hosts.stream().filter(HostResource::isSubordinate).collect(toImmutableList());
|
||||
ImmutableMap<HostResource, String> hostRegistrars =
|
||||
ImmutableList<Host> subordinateHosts =
|
||||
hosts.stream().filter(Host::isSubordinate).collect(toImmutableList());
|
||||
ImmutableMap<Host, String> hostRegistrars =
|
||||
subordinateHosts.isEmpty()
|
||||
? ImmutableMap.of()
|
||||
: tm().transact(
|
||||
@@ -64,7 +64,7 @@ final class NameserverWhoisResponse extends WhoisResponseImpl {
|
||||
|
||||
BasicEmitter emitter = new BasicEmitter();
|
||||
for (int i = 0; i < hosts.size(); i++) {
|
||||
HostResource host = hosts.get(i);
|
||||
Host host = hosts.get(i);
|
||||
String registrarId =
|
||||
host.isSubordinate()
|
||||
? hostRegistrars.get(host)
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<class>google.registry.model.domain.secdns.DomainDsDataHistory</class>
|
||||
<class>google.registry.model.domain.token.AllocationToken</class>
|
||||
<class>google.registry.model.host.HostHistory</class>
|
||||
<class>google.registry.model.host.HostResource</class>
|
||||
<class>google.registry.model.host.Host</class>
|
||||
<class>google.registry.model.poll.PollMessage</class>
|
||||
<class>google.registry.model.poll.PollMessage$OneTime</class>
|
||||
<class>google.registry.model.poll.PollMessage$Autorenew</class>
|
||||
@@ -104,7 +104,7 @@
|
||||
<class>google.registry.model.contact.VKeyConverter_ContactResource</class>
|
||||
<class>google.registry.model.domain.VKeyConverter_Domain</class>
|
||||
<class>google.registry.model.domain.token.VKeyConverter_AllocationToken</class>
|
||||
<class>google.registry.model.host.VKeyConverter_HostResource</class>
|
||||
<class>google.registry.model.host.VKeyConverter_Host</class>
|
||||
<class>google.registry.model.poll.VKeyConverter_Autorenew</class>
|
||||
<class>google.registry.model.poll.VKeyConverter_OneTime</class>
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeSleeper;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import google.registry.util.CapturingLogHandler;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import google.registry.util.JdkLoggerConfig;
|
||||
@@ -57,12 +56,10 @@ public class AsyncTaskEnqueuerTest {
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withCloudSql().withTaskQueue().build();
|
||||
|
||||
@RegisterExtension public final InjectExtension inject = new InjectExtension();
|
||||
|
||||
private AsyncTaskEnqueuer asyncTaskEnqueuer;
|
||||
private final CapturingLogHandler logHandler = new CapturingLogHandler();
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2015-05-18T12:34:56Z"));
|
||||
private CloudTasksHelper cloudTasksHelper = new CloudTasksHelper(clock);
|
||||
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper(clock);
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
|
||||
@@ -34,7 +34,6 @@ import google.registry.model.billing.BillingEvent.Flag;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.ofy.Ofy;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.transaction.QueryComposer.Comparator;
|
||||
@@ -43,7 +42,6 @@ import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeLockHandler;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -59,14 +57,11 @@ class DeleteExpiredDomainsActionTest {
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withCloudSql().withClock(clock).withTaskQueue().build();
|
||||
|
||||
@RegisterExtension public final InjectExtension inject = new InjectExtension();
|
||||
|
||||
private final FakeResponse response = new FakeResponse();
|
||||
private DeleteExpiredDomainsAction action;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
inject.setStaticField(Ofy.class, "clock", clock);
|
||||
createTld("tld");
|
||||
EppController eppController =
|
||||
DaggerEppTestComponent.builder()
|
||||
@@ -103,7 +98,7 @@ class DeleteExpiredDomainsActionTest {
|
||||
|
||||
// A non-autorenewing domain that is past its expiration time and should be deleted.
|
||||
// (This is the only one that needs a full set of subsidiary resources, for the delete flow to
|
||||
// to operate on.)
|
||||
// operate on.)
|
||||
Domain pendingExpirationDomain = persistNonAutorenewingDomain("fizz.tld");
|
||||
|
||||
assertThat(loadByEntity(pendingExpirationDomain).getStatusValues())
|
||||
@@ -184,8 +179,7 @@ class DeleteExpiredDomainsActionTest {
|
||||
.asBuilder()
|
||||
.setAutorenewEndTime(Optional.of(clock.nowUtc().minusDays(10)))
|
||||
.setAutorenewBillingEvent(autorenewBillingEvent.createVKey())
|
||||
.setAutorenewPollMessage(
|
||||
autorenewPollMessage.createVKey(), autorenewPollMessage.getHistoryRevisionId())
|
||||
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
|
||||
.build());
|
||||
|
||||
return pendingExpirationDomain;
|
||||
|
||||
@@ -37,7 +37,7 @@ import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
@@ -95,7 +95,7 @@ public class RelockDomainActionTest {
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
createTlds("tld", "net");
|
||||
HostResource host = persistActiveHost("ns1.example.net");
|
||||
Host host = persistActiveHost("ns1.example.net");
|
||||
domain = persistResource(DatabaseHelper.newDomain(DOMAIN_NAME, host));
|
||||
|
||||
oldLock = domainLockUtils.administrativelyApplyLock(DOMAIN_NAME, CLIENT_ID, POC_ID, false);
|
||||
|
||||
@@ -20,7 +20,6 @@ import static google.registry.batch.AsyncTaskEnqueuer.PARAM_RESOURCE_KEY;
|
||||
import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.newDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistDomainWithDependentResources;
|
||||
import static google.registry.testing.DatabaseHelper.persistDomainWithPendingTransfer;
|
||||
@@ -35,14 +34,12 @@ import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.ofy.Ofy;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -62,16 +59,13 @@ public class ResaveEntityActionTest {
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withCloudSql().withTaskQueue().build();
|
||||
|
||||
@RegisterExtension public final InjectExtension inject = new InjectExtension();
|
||||
|
||||
@Mock private Response response;
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2016-02-11T10:00:00Z"));
|
||||
private AsyncTaskEnqueuer asyncTaskEnqueuer;
|
||||
private CloudTasksHelper cloudTasksHelper = new CloudTasksHelper(clock);
|
||||
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper(clock);
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
inject.setStaticField(Ofy.class, "clock", clock);
|
||||
asyncTaskEnqueuer =
|
||||
AsyncTaskEnqueuerTest.createForTesting(
|
||||
cloudTasksHelper.getTestCloudTasksUtils(), clock, Duration.ZERO);
|
||||
|
||||
+4
-7
@@ -15,7 +15,6 @@
|
||||
package google.registry.batch;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.testing.AppEngineExtension.makeRegistrar1;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
@@ -40,7 +39,6 @@ import google.registry.model.registrar.RegistrarPoc.Type;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import google.registry.util.SelfSignedCaCertificate;
|
||||
import google.registry.util.SendEmailService;
|
||||
import java.security.cert.X509Certificate;
|
||||
@@ -57,11 +55,11 @@ class SendExpiringCertificateNotificationEmailActionTest {
|
||||
|
||||
private static final String EXPIRATION_WARNING_EMAIL_BODY_TEXT =
|
||||
" Dear %1$s,\n"
|
||||
+ "\n"
|
||||
+ '\n'
|
||||
+ "We would like to inform you that your %2$s certificate will expire at %3$s."
|
||||
+ "\n"
|
||||
+ '\n'
|
||||
+ " Kind update your account using the following steps: "
|
||||
+ "\n"
|
||||
+ '\n'
|
||||
+ " 1. Navigate to support and login using your %4$s@registry.example credentials.\n"
|
||||
+ " 2. Click Settings -> Privacy on the top left corner.\n"
|
||||
+ " 3. Click Edit and enter certificate string."
|
||||
@@ -75,7 +73,6 @@ class SendExpiringCertificateNotificationEmailActionTest {
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withCloudSql().withTaskQueue().build();
|
||||
|
||||
@RegisterExtension public final InjectExtension inject = new InjectExtension();
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2021-05-24T20:21:22Z"));
|
||||
private final SendEmailService sendEmailService = mock(SendEmailService.class);
|
||||
private CertificateChecker certificateChecker;
|
||||
@@ -708,7 +705,7 @@ class SendExpiringCertificateNotificationEmailActionTest {
|
||||
}
|
||||
|
||||
/** Returns persisted sample contacts with a customized contact email type. */
|
||||
private ImmutableList<RegistrarPoc> persistSampleContacts(
|
||||
private static ImmutableList<RegistrarPoc> persistSampleContacts(
|
||||
Registrar registrar, RegistrarPoc.Type emailType) {
|
||||
return persistSimpleResources(
|
||||
ImmutableList.of(
|
||||
|
||||
@@ -38,7 +38,6 @@ import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -102,7 +101,6 @@ class WipeOutContactHistoryPiiActionTest {
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withCloudSql().withTaskQueue().build();
|
||||
|
||||
@RegisterExtension public final InjectExtension inject = new InjectExtension();
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2021-08-26T20:21:22Z"));
|
||||
|
||||
private FakeResponse response;
|
||||
|
||||
@@ -62,9 +62,9 @@ import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.host.HostBase;
|
||||
import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.rde.RdeMode;
|
||||
import google.registry.model.rde.RdeRevision;
|
||||
import google.registry.model.rde.RdeRevision.RdeRevisionId;
|
||||
@@ -272,7 +272,7 @@ public class RdePipelineTest {
|
||||
|
||||
// This host is never referenced.
|
||||
persistHostHistory(persistActiveHost("ns0.domain.tld"));
|
||||
HostResource host1 = persistActiveHost("ns1.external.tld");
|
||||
Host host1 = persistActiveHost("ns1.external.tld");
|
||||
persistHostHistory(host1);
|
||||
Domain helloDomain =
|
||||
persistEppResource(
|
||||
@@ -282,7 +282,7 @@ public class RdePipelineTest {
|
||||
.build());
|
||||
persistDomainHistory(helloDomain);
|
||||
persistHostHistory(persistActiveHost("not-used-subordinate.hello.soy"));
|
||||
HostResource host2 = persistActiveHost("ns1.hello.soy");
|
||||
Host host2 = persistActiveHost("ns1.hello.soy");
|
||||
persistHostHistory(host2);
|
||||
Domain kittyDomain =
|
||||
persistEppResource(
|
||||
@@ -306,7 +306,7 @@ public class RdePipelineTest {
|
||||
persistContactHistory(contact3);
|
||||
// This is a subordinate domain in TLD .cat, which is not included in any pending deposit. But
|
||||
// it should still be included as a subordinate host in the pendign deposit for .soy.
|
||||
HostResource host3 = persistActiveHost("ns1.lol.cat");
|
||||
Host host3 = persistActiveHost("ns1.lol.cat");
|
||||
persistHostHistory(host3);
|
||||
persistDomainHistory(
|
||||
helloDomain
|
||||
@@ -331,7 +331,7 @@ public class RdePipelineTest {
|
||||
persistDomainHistory(kittyDomain.asBuilder().setDeletionTime(clock.nowUtc()).build());
|
||||
ContactResource futureContact = persistActiveContact("future-contact");
|
||||
persistContactHistory(futureContact);
|
||||
HostResource futureHost = persistActiveHost("ns1.future.tld");
|
||||
Host futureHost = persistActiveHost("ns1.future.tld");
|
||||
persistHostHistory(futureHost);
|
||||
persistDomainHistory(
|
||||
persistEppResource(
|
||||
|
||||
@@ -24,13 +24,11 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import google.registry.model.ofy.Ofy;
|
||||
import google.registry.request.HttpException.NotFoundException;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.CloudTasksHelper.CloudTasksHelperModule;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -47,8 +45,6 @@ public final class DnsInjectionTest {
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withCloudSql().withTaskQueue().build();
|
||||
|
||||
@RegisterExtension public final InjectExtension inject = new InjectExtension();
|
||||
|
||||
private final HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
private final HttpServletResponse rsp = mock(HttpServletResponse.class);
|
||||
private final StringWriter httpOutput = new StringWriter();
|
||||
@@ -58,7 +54,6 @@ public final class DnsInjectionTest {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
inject.setStaticField(Ofy.class, "clock", clock);
|
||||
when(rsp.getWriter()).thenReturn(new PrintWriter(httpOutput));
|
||||
component =
|
||||
DaggerDnsTestComponent.builder()
|
||||
|
||||
@@ -38,6 +38,7 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.dns.DnsMetrics.ActionStatus;
|
||||
@@ -45,7 +46,6 @@ import google.registry.dns.DnsMetrics.CommitStatus;
|
||||
import google.registry.dns.DnsMetrics.PublishStatus;
|
||||
import google.registry.dns.writer.DnsWriter;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.ofy.Ofy;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.request.HttpException.ServiceUnavailableException;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
@@ -55,14 +55,18 @@ import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeLockHandler;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import google.registry.ui.server.SendEmailUtils;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.SendEmailService;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
/** Unit tests for {@link PublishDnsUpdatesAction}. */
|
||||
public class PublishDnsUpdatesActionTest {
|
||||
@@ -71,7 +75,6 @@ public class PublishDnsUpdatesActionTest {
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withCloudSql().withTaskQueue().build();
|
||||
|
||||
@RegisterExtension public final InjectExtension inject = new InjectExtension();
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("1971-01-01TZ"));
|
||||
private final FakeResponse response = new FakeResponse();
|
||||
private final FakeLockHandler lockHandler = new FakeLockHandler(true);
|
||||
@@ -80,10 +83,18 @@ public class PublishDnsUpdatesActionTest {
|
||||
private final DnsQueue dnsQueue = mock(DnsQueue.class);
|
||||
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
|
||||
private PublishDnsUpdatesAction action;
|
||||
private final SendEmailService emailService = mock(SendEmailService.class);
|
||||
private SendEmailUtils sendEmailUtils;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
inject.setStaticField(Ofy.class, "clock", clock);
|
||||
void beforeEach() throws Exception {
|
||||
sendEmailUtils =
|
||||
new SendEmailUtils(
|
||||
new InternetAddress("outgoing@registry.example"),
|
||||
"UnitTest Registry",
|
||||
ImmutableList.of("notification@test.example", "notification2@test.example"),
|
||||
emailService);
|
||||
|
||||
createTld("xn--q9jyb4c");
|
||||
persistResource(
|
||||
Registry.get("xn--q9jyb4c")
|
||||
@@ -142,6 +153,10 @@ public class PublishDnsUpdatesActionTest {
|
||||
hosts,
|
||||
tld,
|
||||
Duration.standardSeconds(10),
|
||||
"Subj",
|
||||
"Body %1$s %2$s %3$s %4$s %5$s",
|
||||
"registry@test.com",
|
||||
"awesomeRegistry",
|
||||
Optional.ofNullable(retryCount),
|
||||
Optional.empty(),
|
||||
dnsQueue,
|
||||
@@ -150,6 +165,7 @@ public class PublishDnsUpdatesActionTest {
|
||||
lockHandler,
|
||||
clock,
|
||||
cloudTasksHelper.getTestCloudTasksUtils(),
|
||||
sendEmailUtils,
|
||||
response);
|
||||
}
|
||||
|
||||
@@ -385,6 +401,37 @@ public class PublishDnsUpdatesActionTest {
|
||||
assertThat(response.getStatus()).isEqualTo(SC_ACCEPTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDomainDnsUpdateRetriesExhausted_emailIsSent() {
|
||||
action =
|
||||
createAction("xn--q9jyb4c", ImmutableSet.of("example.xn--q9jyb4c"), ImmutableSet.of(), 10);
|
||||
doThrow(new RuntimeException()).when(dnsWriter).commit();
|
||||
action.run();
|
||||
ArgumentCaptor<EmailMessage> contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
|
||||
verify(emailService).sendEmail(contentCaptor.capture());
|
||||
EmailMessage emailMessage = contentCaptor.getValue();
|
||||
assertThat(emailMessage.subject()).isEqualTo("Subj");
|
||||
assertThat(emailMessage.body())
|
||||
.isEqualTo(
|
||||
"Body The Registrar example.xn--q9jyb4c domain awesomeRegistry registry@test.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHostDnsUpdateRetriesExhausted_emailIsSent() {
|
||||
action =
|
||||
createAction(
|
||||
"xn--q9jyb4c", ImmutableSet.of(), ImmutableSet.of("ns1.example.xn--q9jyb4c"), 10);
|
||||
doThrow(new RuntimeException()).when(dnsWriter).commit();
|
||||
action.run();
|
||||
ArgumentCaptor<EmailMessage> contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
|
||||
verify(emailService).sendEmail(contentCaptor.capture());
|
||||
EmailMessage emailMessage = contentCaptor.getValue();
|
||||
assertThat(emailMessage.subject()).isEqualTo("Subj");
|
||||
assertThat(emailMessage.body())
|
||||
.isEqualTo(
|
||||
"Body The Registrar ns1.example.xn--q9jyb4c host awesomeRegistry registry@test.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTaskMissingRetryHeaders_throwsException() {
|
||||
IllegalStateException thrown =
|
||||
|
||||
@@ -18,7 +18,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.io.BaseEncoding.base16;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.newHostResource;
|
||||
import static google.registry.testing.DatabaseHelper.newHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
@@ -41,7 +41,7 @@ import google.registry.dns.writer.clouddns.CloudDnsWriter.ZoneStateException;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
@@ -283,31 +283,28 @@ public class CloudDnsWriterTest {
|
||||
|
||||
/** Returns a domain to be persisted in Datastore. */
|
||||
private static Domain fakeDomain(
|
||||
String domainName, ImmutableSet<HostResource> nameservers, int numDsRecords) {
|
||||
String domainName, ImmutableSet<Host> nameservers, int numDsRecords) {
|
||||
ImmutableSet.Builder<DelegationSignerData> dsDataBuilder = new ImmutableSet.Builder<>();
|
||||
|
||||
for (int i = 0; i < numDsRecords; i++) {
|
||||
dsDataBuilder.add(DelegationSignerData.create(i, 3, 1, base16().decode("1234567890ABCDEF")));
|
||||
}
|
||||
|
||||
ImmutableSet.Builder<VKey<HostResource>> hostResourceRefBuilder = new ImmutableSet.Builder<>();
|
||||
for (HostResource nameserver : nameservers) {
|
||||
hostResourceRefBuilder.add(nameserver.createVKey());
|
||||
ImmutableSet.Builder<VKey<Host>> hostRefBuilder = new ImmutableSet.Builder<>();
|
||||
for (Host nameserver : nameservers) {
|
||||
hostRefBuilder.add(nameserver.createVKey());
|
||||
}
|
||||
|
||||
return DatabaseHelper.newDomain(domainName)
|
||||
.asBuilder()
|
||||
.setNameservers(hostResourceRefBuilder.build())
|
||||
.setNameservers(hostRefBuilder.build())
|
||||
.setDsData(dsDataBuilder.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Returns a nameserver used for its NS record. */
|
||||
private static HostResource fakeHost(String nameserver, InetAddress... addresses) {
|
||||
return newHostResource(nameserver)
|
||||
.asBuilder()
|
||||
.setInetAddresses(ImmutableSet.copyOf(addresses))
|
||||
.build();
|
||||
private static Host fakeHost(String nameserver, InetAddress... addresses) {
|
||||
return newHost(nameserver).asBuilder().setInetAddresses(ImmutableSet.copyOf(addresses)).build();
|
||||
}
|
||||
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
|
||||
@@ -18,7 +18,7 @@ import static com.google.common.io.BaseEncoding.base16;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.newHostResource;
|
||||
import static google.registry.testing.DatabaseHelper.newHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveSubordinateHost;
|
||||
@@ -38,14 +38,13 @@ import com.google.common.net.InetAddresses;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.ofy.Ofy;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -76,8 +75,6 @@ public class DnsUpdateWriterTest {
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withCloudSql().withTaskQueue().build();
|
||||
|
||||
@RegisterExtension public final InjectExtension inject = new InjectExtension();
|
||||
|
||||
@Mock private DnsMessageTransport mockResolver;
|
||||
@Captor private ArgumentCaptor<Update> updateCaptor;
|
||||
|
||||
@@ -87,8 +84,6 @@ public class DnsUpdateWriterTest {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
inject.setStaticField(Ofy.class, "clock", clock);
|
||||
|
||||
createTld("tld");
|
||||
when(mockResolver.send(any(Update.class))).thenReturn(messageWithResponseCode(Rcode.NOERROR));
|
||||
|
||||
@@ -98,8 +93,8 @@ public class DnsUpdateWriterTest {
|
||||
|
||||
@Test
|
||||
void testPublishDomainCreate_publishesNameServers() throws Exception {
|
||||
HostResource host1 = persistActiveHost("ns1.example.tld");
|
||||
HostResource host2 = persistActiveHost("ns2.example.tld");
|
||||
Host host1 = persistActiveHost("ns1.example.tld");
|
||||
Host host2 = persistActiveHost("ns2.example.tld");
|
||||
Domain domain =
|
||||
persistActiveDomain("example.tld")
|
||||
.asBuilder()
|
||||
@@ -121,7 +116,7 @@ public class DnsUpdateWriterTest {
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@Test
|
||||
void testPublishAtomic_noCommit() {
|
||||
HostResource host1 = persistActiveHost("ns.example1.tld");
|
||||
Host host1 = persistActiveHost("ns.example1.tld");
|
||||
Domain domain1 =
|
||||
persistActiveDomain("example1.tld")
|
||||
.asBuilder()
|
||||
@@ -129,7 +124,7 @@ public class DnsUpdateWriterTest {
|
||||
.build();
|
||||
persistResource(domain1);
|
||||
|
||||
HostResource host2 = persistActiveHost("ns.example2.tld");
|
||||
Host host2 = persistActiveHost("ns.example2.tld");
|
||||
Domain domain2 =
|
||||
persistActiveDomain("example2.tld")
|
||||
.asBuilder()
|
||||
@@ -145,7 +140,7 @@ public class DnsUpdateWriterTest {
|
||||
|
||||
@Test
|
||||
void testPublishAtomic_oneUpdate() throws Exception {
|
||||
HostResource host1 = persistActiveHost("ns.example1.tld");
|
||||
Host host1 = persistActiveHost("ns.example1.tld");
|
||||
Domain domain1 =
|
||||
persistActiveDomain("example1.tld")
|
||||
.asBuilder()
|
||||
@@ -153,7 +148,7 @@ public class DnsUpdateWriterTest {
|
||||
.build();
|
||||
persistResource(domain1);
|
||||
|
||||
HostResource host2 = persistActiveHost("ns.example2.tld");
|
||||
Host host2 = persistActiveHost("ns.example2.tld");
|
||||
Domain domain2 =
|
||||
persistActiveDomain("example2.tld")
|
||||
.asBuilder()
|
||||
@@ -235,9 +230,9 @@ public class DnsUpdateWriterTest {
|
||||
|
||||
@Test
|
||||
void testPublishHostCreate_publishesAddressRecords() throws Exception {
|
||||
HostResource host =
|
||||
Host host =
|
||||
persistResource(
|
||||
newHostResource("ns1.example.tld")
|
||||
newHost("ns1.example.tld")
|
||||
.asBuilder()
|
||||
.setInetAddresses(
|
||||
ImmutableSet.of(
|
||||
@@ -305,10 +300,10 @@ public class DnsUpdateWriterTest {
|
||||
|
||||
@Test
|
||||
void testPublishDomainExternalAndInBailiwickNameServer() throws Exception {
|
||||
HostResource externalNameserver = persistResource(newHostResource("ns1.example.com"));
|
||||
HostResource inBailiwickNameserver =
|
||||
Host externalNameserver = persistResource(newHost("ns1.example.com"));
|
||||
Host inBailiwickNameserver =
|
||||
persistResource(
|
||||
newHostResource("ns1.example.tld")
|
||||
newHost("ns1.example.tld")
|
||||
.asBuilder()
|
||||
.setInetAddresses(
|
||||
ImmutableSet.of(
|
||||
@@ -342,9 +337,9 @@ public class DnsUpdateWriterTest {
|
||||
|
||||
@Test
|
||||
void testPublishDomainDeleteOrphanGlues() throws Exception {
|
||||
HostResource inBailiwickNameserver =
|
||||
Host inBailiwickNameserver =
|
||||
persistResource(
|
||||
newHostResource("ns1.example.tld")
|
||||
newHost("ns1.example.tld")
|
||||
.asBuilder()
|
||||
.setInetAddresses(
|
||||
ImmutableSet.of(
|
||||
@@ -401,7 +396,7 @@ public class DnsUpdateWriterTest {
|
||||
@SuppressWarnings("AssertThrowsMultipleStatements")
|
||||
@Test
|
||||
void testPublishHostFails_whenDnsUpdateReturnsError() throws Exception {
|
||||
HostResource host =
|
||||
Host host =
|
||||
persistActiveSubordinateHost("ns1.example.tld", persistActiveDomain("example.tld"))
|
||||
.asBuilder()
|
||||
.setInetAddresses(ImmutableSet.of(InetAddresses.forString("10.0.0.1")))
|
||||
@@ -418,23 +413,23 @@ public class DnsUpdateWriterTest {
|
||||
assertThat(thrown).hasMessageThat().contains("SERVFAIL");
|
||||
}
|
||||
|
||||
private void assertThatUpdatedZoneIs(Update update, String zoneName) {
|
||||
Record[] zoneRecords = update.getSectionArray(Section.ZONE);
|
||||
assertThat(zoneRecords[0].getName().toString()).isEqualTo(zoneName);
|
||||
private static void assertThatUpdatedZoneIs(Update update, String zoneName) {
|
||||
List<Record> zoneRecords = update.getSection(Section.ZONE);
|
||||
assertThat(zoneRecords.get(0).getName().toString()).isEqualTo(zoneName);
|
||||
}
|
||||
|
||||
private void assertThatTotalUpdateSetsIs(Update update, int count) {
|
||||
private static void assertThatTotalUpdateSetsIs(Update update, int count) {
|
||||
assertThat(update.getSectionRRsets(Section.UPDATE)).hasSize(count);
|
||||
}
|
||||
|
||||
private void assertThatUpdateDeletes(Update update, String resourceName, int recordType) {
|
||||
private static void assertThatUpdateDeletes(Update update, String resourceName, int recordType) {
|
||||
ImmutableList<Record> deleted = findUpdateRecords(update, resourceName, recordType);
|
||||
// There's only an empty (i.e. "delete") record.
|
||||
assertThat(deleted.get(0).rdataToString()).hasLength(0);
|
||||
assertThat(deleted).hasSize(1);
|
||||
}
|
||||
|
||||
private void assertThatUpdateAdds(
|
||||
private static void assertThatUpdateAdds(
|
||||
Update update, String resourceName, int recordType, String... resourceData) {
|
||||
ArrayList<String> expectedData = new ArrayList<>();
|
||||
Collections.addAll(expectedData, resourceData);
|
||||
@@ -446,7 +441,7 @@ public class DnsUpdateWriterTest {
|
||||
assertThat(actualData).containsExactlyElementsIn(expectedData);
|
||||
}
|
||||
|
||||
private ImmutableList<Record> findUpdateRecords(
|
||||
private static ImmutableList<Record> findUpdateRecords(
|
||||
Update update, String resourceName, int recordType) {
|
||||
for (RRset set : update.getSectionRRsets(Section.UPDATE)) {
|
||||
if (set.getName().toString().equals(resourceName) && set.getType() == recordType) {
|
||||
@@ -460,7 +455,7 @@ public class DnsUpdateWriterTest {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
private Message messageWithResponseCode(int responseCode) {
|
||||
private static Message messageWithResponseCode(int responseCode) {
|
||||
Message message = new Message();
|
||||
message.getHeader().setOpcode(Opcode.UPDATE);
|
||||
message.getHeader().setFlag(Flags.QR);
|
||||
|
||||
@@ -33,16 +33,13 @@ import com.google.cloud.storage.contrib.nio.testing.LocalStorageHelper;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.gcs.GcsUtils;
|
||||
import google.registry.model.ofy.Ofy;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.storage.drive.DriveConnection;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
@@ -51,8 +48,8 @@ import org.mockito.ArgumentCaptor;
|
||||
class ExportDomainListsActionTest {
|
||||
|
||||
private final GcsUtils gcsUtils = new GcsUtils(LocalStorageHelper.getOptions());
|
||||
private DriveConnection driveConnection = mock(DriveConnection.class);
|
||||
private ArgumentCaptor<byte[]> bytesExportedToDrive = ArgumentCaptor.forClass(byte[].class);
|
||||
private final DriveConnection driveConnection = mock(DriveConnection.class);
|
||||
private final ArgumentCaptor<byte[]> bytesExportedToDrive = ArgumentCaptor.forClass(byte[].class);
|
||||
private ExportDomainListsAction action;
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2020-02-02T02:02:02Z"));
|
||||
|
||||
@@ -60,11 +57,6 @@ class ExportDomainListsActionTest {
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withCloudSql().withLocalModules().withTaskQueue().build();
|
||||
|
||||
@Order(Order.DEFAULT - 1)
|
||||
@RegisterExtension
|
||||
public final InjectExtension inject =
|
||||
new InjectExtension().withStaticFieldOverride(Ofy.class, "clock", clock);
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
|
||||
@@ -42,7 +42,6 @@ import google.registry.request.Response;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeSleeper;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import google.registry.util.Retrier;
|
||||
import java.io.IOException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -59,8 +58,6 @@ public class SyncGroupMembersActionTest {
|
||||
@RegisterExtension
|
||||
public final AppEngineExtension appEngine = AppEngineExtension.builder().withCloudSql().build();
|
||||
|
||||
@RegisterExtension public final InjectExtension inject = new InjectExtension();
|
||||
|
||||
private final DirectoryGroupsConnection connection = mock(DirectoryGroupsConnection.class);
|
||||
private final Response response = mock(Response.class);
|
||||
|
||||
@@ -76,8 +73,7 @@ public class SyncGroupMembersActionTest {
|
||||
@Test
|
||||
void test_getGroupEmailAddressForContactType_convertsToLowercase() {
|
||||
assertThat(
|
||||
getGroupEmailAddressForContactType(
|
||||
"SomeRegistrar", RegistrarPoc.Type.ADMIN, "domain-registry.example"))
|
||||
getGroupEmailAddressForContactType("SomeRegistrar", ADMIN, "domain-registry.example"))
|
||||
.isEqualTo("someregistrar-primary-contacts@domain-registry.example");
|
||||
}
|
||||
|
||||
|
||||
@@ -35,14 +35,12 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.common.Cursor;
|
||||
import google.registry.model.ofy.Ofy;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -60,8 +58,6 @@ public class SyncRegistrarsSheetTest {
|
||||
@RegisterExtension
|
||||
public final AppEngineExtension appEngine = AppEngineExtension.builder().withCloudSql().build();
|
||||
|
||||
@RegisterExtension public final InjectExtension inject = new InjectExtension();
|
||||
|
||||
@Captor private ArgumentCaptor<ImmutableList<ImmutableMap<String, String>>> rowsCaptor;
|
||||
@Mock private SheetSynchronizer sheetSynchronizer;
|
||||
|
||||
@@ -76,7 +72,6 @@ public class SyncRegistrarsSheetTest {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
inject.setStaticField(Ofy.class, "clock", clock);
|
||||
createTld("example");
|
||||
// Remove Registrar entities created by AppEngineExtension (and RegistrarContact's, for jpa).
|
||||
// We don't do this for ofy because ofy's loadAllOf() can't be called in a transaction but
|
||||
@@ -215,7 +210,7 @@ public class SyncRegistrarsSheetTest {
|
||||
+ "Phone number and email visible in domain WHOIS query as "
|
||||
+ "Registrar Abuse contact info: No\n"
|
||||
+ "Registrar-Console access: No\n"
|
||||
+ "\n"
|
||||
+ '\n'
|
||||
+ "John Doe\n"
|
||||
+ "john.doe@example.tld\n"
|
||||
+ "Tel: +1.1234567890\n"
|
||||
|
||||
@@ -95,7 +95,7 @@ class EppLifecycleContactTest extends EppTestCase {
|
||||
.hasCommandName("PollRequest")
|
||||
.and()
|
||||
.hasStatus(SUCCESS_WITH_ACK_MESSAGE);
|
||||
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "2-1-ROID-3-6-2000"))
|
||||
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "6-2000"))
|
||||
.atTime("2000-06-08T22:02:00Z")
|
||||
.hasResponse("poll_ack_response_empty.xml");
|
||||
assertThat(getRecordedEppMetric())
|
||||
|
||||
@@ -747,11 +747,11 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
.hasResponse(
|
||||
"poll_response_autorenew.xml",
|
||||
ImmutableMap.of(
|
||||
"ID", "1-B-EXAMPLE-12-15-2002",
|
||||
"ID", "15-2002",
|
||||
"QDATE", "2002-06-01T00:04:00Z",
|
||||
"DOMAIN", "fakesite.example",
|
||||
"EXDATE", "2003-06-01T00:04:00Z"));
|
||||
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-B-EXAMPLE-12-15-2002"))
|
||||
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "15-2002"))
|
||||
.atTime("2002-07-01T00:02:00Z")
|
||||
.hasResponse("poll_ack_response_empty.xml");
|
||||
|
||||
@@ -765,13 +765,13 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
.hasResponse(
|
||||
"poll_response_autorenew.xml",
|
||||
ImmutableMap.of(
|
||||
"ID", "1-B-EXAMPLE-12-15-2003", // Note -- Year is different from previous ID.
|
||||
"ID", "15-2003", // Note -- Year is different from previous ID.
|
||||
"QDATE", "2003-06-01T00:04:00Z",
|
||||
"DOMAIN", "fakesite.example",
|
||||
"EXDATE", "2004-06-01T00:04:00Z"));
|
||||
|
||||
// Ack the second poll message and verify that none remain.
|
||||
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-B-EXAMPLE-12-15-2003"))
|
||||
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "15-2003"))
|
||||
.atTime("2003-07-01T00:05:05Z")
|
||||
.hasResponse("poll_ack_response_empty.xml");
|
||||
assertThatCommand("poll.xml")
|
||||
@@ -801,7 +801,7 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
|
||||
// As the losing registrar, read the request poll message, and then ack it.
|
||||
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
|
||||
String messageId = "1-B-EXAMPLE-18-24-2001";
|
||||
String messageId = "24-2001";
|
||||
assertThatCommand("poll.xml")
|
||||
.atTime("2001-01-01T00:01:00Z")
|
||||
.hasResponse("poll_response_domain_transfer_request.xml", ImmutableMap.of("ID", messageId));
|
||||
@@ -810,7 +810,7 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
.hasResponse("poll_ack_response_empty.xml");
|
||||
|
||||
// Five days in the future, expect a server approval poll message to the loser, and ack it.
|
||||
messageId = "1-B-EXAMPLE-18-23-2001";
|
||||
messageId = "23-2001";
|
||||
assertThatCommand("poll.xml")
|
||||
.atTime("2001-01-06T00:01:00Z")
|
||||
.hasResponse(
|
||||
@@ -822,7 +822,7 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
assertThatLogoutSucceeds();
|
||||
|
||||
// Also expect a server approval poll message to the winner, with the transfer request trid.
|
||||
messageId = "1-B-EXAMPLE-18-22-2001";
|
||||
messageId = "22-2001";
|
||||
assertThatLoginSucceeds("TheRegistrar", "password2");
|
||||
assertThatCommand("poll.xml")
|
||||
.atTime("2001-01-06T00:02:00Z")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user