mirror of
https://github.com/google/nomulus
synced 2026-07-08 17:16:54 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a557b3f376 | |||
| f4a49864b5 | |||
| acdecca181 | |||
| 5264ab3fc3 | |||
| a9d59e4d6e | |||
| 1d3738da27 | |||
| 82a3a49268 | |||
| 5bbad483e4 | |||
| f6e9dae58d | |||
| c4c1c72306 | |||
| 775f672f2a | |||
| 372c854268 |
@@ -707,6 +707,10 @@ createToolTask(
|
||||
createToolTask(
|
||||
'jpaDemoPipeline', 'google.registry.beam.common.JpaDemoPipeline')
|
||||
|
||||
createToolTask(
|
||||
'createSyntheticDomainHistories',
|
||||
'google.registry.tools.javascrap.CreateSyntheticDomainHistoriesPipeline')
|
||||
|
||||
project.tasks.create('generateSqlSchema', JavaExec) {
|
||||
classpath = sourceSets.nonprod.runtimeClasspath
|
||||
main = 'google.registry.tools.DevTool'
|
||||
|
||||
@@ -91,7 +91,7 @@ public class DeleteProberDataAction implements Runnable {
|
||||
// Note: creationTime must be compared to a Java object (CreateAutoTimestamp) but deletionTime can
|
||||
// be compared directly to the SQL timestamp (it's a DateTime)
|
||||
private static final String DOMAIN_QUERY_STRING =
|
||||
"FROM Domain d WHERE d.tld IN :tlds AND d.fullyQualifiedDomainName NOT LIKE 'nic.%' AND"
|
||||
"FROM Domain d WHERE d.tld IN :tlds AND d.domainName NOT LIKE 'nic.%' AND"
|
||||
+ " (d.subordinateHosts IS EMPTY OR d.subordinateHosts IS NULL) AND d.creationTime <"
|
||||
+ " :creationTimeCutoff AND ((d.creationTime <= :nowAutoTimestamp AND d.deletionTime >"
|
||||
+ " current_timestamp()) OR d.deletionTime < :nowMinusSoftDeleteDelay) ORDER BY d.repoId";
|
||||
@@ -220,7 +220,7 @@ public class DeleteProberDataAction implements Runnable {
|
||||
private void hardDeleteDomainsAndHosts(
|
||||
ImmutableList<String> domainRepoIds, ImmutableList<String> hostNames) {
|
||||
jpaTm()
|
||||
.query("DELETE FROM Host WHERE fullyQualifiedHostName IN :hostNames")
|
||||
.query("DELETE FROM Host WHERE hostName IN :hostNames")
|
||||
.setParameter("hostNames", hostNames)
|
||||
.executeUpdate();
|
||||
jpaTm()
|
||||
|
||||
@@ -301,7 +301,7 @@ public class RdePipeline implements Serializable {
|
||||
.apply(
|
||||
"Read all production Registrars",
|
||||
RegistryJpaIO.read(
|
||||
"SELECT clientIdentifier FROM Registrar WHERE type NOT IN (:types)",
|
||||
"SELECT registrarId FROM Registrar WHERE type NOT IN (:types)",
|
||||
ImmutableMap.of("types", IGNORED_REGISTRAR_TYPES),
|
||||
String.class,
|
||||
id -> VKey.createSql(Registrar.class, id)))
|
||||
|
||||
@@ -75,8 +75,8 @@ public class SafeBrowsingTransforms {
|
||||
private final String apiKey;
|
||||
|
||||
/**
|
||||
* Maps a domain name's {@code fullyQualifiedDomainName} to its corresponding {@link
|
||||
* DomainNameInfo} to facilitate batching SafeBrowsing API requests.
|
||||
* Maps a domain name's {@code domainName} to its corresponding {@link DomainNameInfo} to
|
||||
* facilitate batching SafeBrowsing API requests.
|
||||
*/
|
||||
private final Map<String, DomainNameInfo> domainNameInfoBuffer =
|
||||
new LinkedHashMap<>(BATCH_SIZE);
|
||||
@@ -186,8 +186,8 @@ public class SafeBrowsingTransforms {
|
||||
private JSONObject createRequestBody() throws JSONException {
|
||||
// Accumulate all domain names to evaluate.
|
||||
JSONArray threatArray = new JSONArray();
|
||||
for (String fullyQualifiedDomainName : domainNameInfoBuffer.keySet()) {
|
||||
threatArray.put(new JSONObject().put("url", fullyQualifiedDomainName));
|
||||
for (String domainName : domainNameInfoBuffer.keySet()) {
|
||||
threatArray.put(new JSONObject().put("url", domainName));
|
||||
}
|
||||
// Construct the JSON request body
|
||||
return new JSONObject()
|
||||
|
||||
@@ -113,7 +113,7 @@ public class Spec11Pipeline implements Serializable {
|
||||
Read<Object[], KV<String, String>> read =
|
||||
RegistryJpaIO.read(
|
||||
"select d.repoId, r.emailAddress from Domain d join Registrar r on"
|
||||
+ " d.currentSponsorClientId = r.clientIdentifier where r.type = 'REAL' and"
|
||||
+ " d.currentSponsorClientId = r.registrarId where r.type = 'REAL' and"
|
||||
+ " d.deletionTime > now()",
|
||||
false,
|
||||
Spec11Pipeline::parseRow);
|
||||
|
||||
@@ -25,23 +25,23 @@ import org.json.JSONObject;
|
||||
public abstract class ThreatMatch implements Serializable {
|
||||
|
||||
private static final String THREAT_TYPE_FIELD = "threatType";
|
||||
private static final String DOMAIN_NAME_FIELD = "fullyQualifiedDomainName";
|
||||
private static final String DOMAIN_NAME_FIELD = "domainName";
|
||||
|
||||
/** Returns what kind of threat it is (malware, phishing etc.) */
|
||||
public abstract String threatType();
|
||||
/** Returns the fully qualified domain name [SLD].[TLD] of the matched threat. */
|
||||
public abstract String fullyQualifiedDomainName();
|
||||
public abstract String domainName();
|
||||
|
||||
@VisibleForTesting
|
||||
static ThreatMatch create(String threatType, String fullyQualifiedDomainName) {
|
||||
return new AutoValue_ThreatMatch(threatType, fullyQualifiedDomainName);
|
||||
static ThreatMatch create(String threatType, String domainName) {
|
||||
return new AutoValue_ThreatMatch(threatType, domainName);
|
||||
}
|
||||
|
||||
/** Returns a {@link JSONObject} representing a subset of this object's data. */
|
||||
JSONObject toJSON() throws JSONException {
|
||||
return new JSONObject()
|
||||
.put(THREAT_TYPE_FIELD, threatType())
|
||||
.put(DOMAIN_NAME_FIELD, fullyQualifiedDomainName());
|
||||
.put(DOMAIN_NAME_FIELD, domainName());
|
||||
}
|
||||
|
||||
/** Parses a {@link JSONObject} and returns an equivalent {@link ThreatMatch}. */
|
||||
|
||||
@@ -37,7 +37,7 @@ import google.registry.dns.writer.BaseDnsWriter;
|
||||
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.domain.secdns.DomainDsData;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.util.Clock;
|
||||
@@ -134,10 +134,10 @@ public class CloudDnsWriter extends BaseDnsWriter {
|
||||
ImmutableSet.Builder<ResourceRecordSet> domainRecords = new ImmutableSet.Builder<>();
|
||||
|
||||
// Construct DS records (if any).
|
||||
Set<DelegationSignerData> dsData = domain.get().getDsData();
|
||||
Set<DomainDsData> dsData = domain.get().getDsData();
|
||||
if (!dsData.isEmpty()) {
|
||||
HashSet<String> dsRrData = new HashSet<>();
|
||||
for (DelegationSignerData ds : dsData) {
|
||||
for (DomainDsData ds : dsData) {
|
||||
dsRrData.add(ds.toRrData());
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ import google.registry.config.RegistryConfig.Config;
|
||||
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.domain.secdns.DomainDsData;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.util.Clock;
|
||||
@@ -185,7 +185,7 @@ public class DnsUpdateWriter extends BaseDnsWriter {
|
||||
|
||||
private RRset makeDelegationSignerSet(Domain domain) {
|
||||
RRset signerSet = new RRset();
|
||||
for (DelegationSignerData signerData : domain.getDsData()) {
|
||||
for (DomainDsData signerData : domain.getDsData()) {
|
||||
DSRecord dsRecord =
|
||||
new DSRecord(
|
||||
toAbsoluteName(domain.getDomainName()),
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<!-- TODO(b/249863289): disable until it is safe to run this pipeline
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResourcesPipeline?fast=true]]></url>
|
||||
<description>
|
||||
@@ -110,6 +111,7 @@
|
||||
<schedule>1st monday of month 09:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
-->
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/updateRegistrarRdapBaseUrls]]></url>
|
||||
@@ -267,7 +269,7 @@
|
||||
about 2 hours to complete, so we give 11 hours to be safe. Normally, we give 24+ hours (see
|
||||
icannReportingStaging), but the invoicing team prefers receiving the e-mail on the first of
|
||||
each month. -->
|
||||
<schedule>1 of month 17:00</schedule>
|
||||
<schedule>1 of month 19:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<!-- TODO(b/249863289): disable until it is safe to run this pipeline
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResourcesPipeline?fast=true]]></url>
|
||||
<description>
|
||||
@@ -94,6 +95,7 @@
|
||||
<schedule>1st monday of month 09:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
-->
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportDomainLists&runInEmpty]]></url>
|
||||
|
||||
@@ -91,10 +91,10 @@ public class ExportDomainListsAction implements Runnable {
|
||||
// field that compares with the substituted value.
|
||||
jpaTm()
|
||||
.query(
|
||||
"SELECT fullyQualifiedDomainName FROM Domain "
|
||||
"SELECT domainName FROM Domain "
|
||||
+ "WHERE tld = :tld "
|
||||
+ "AND deletionTime > :now "
|
||||
+ "ORDER by fullyQualifiedDomainName ASC",
|
||||
+ "ORDER by domainName ASC",
|
||||
String.class)
|
||||
.setParameter("tld", tld)
|
||||
.setParameter("now", clock.nowUtc())
|
||||
|
||||
@@ -114,7 +114,7 @@ class SyncRegistrarsSheet {
|
||||
// and you'll need to remove deleted columns probably like a week after
|
||||
// deployment.
|
||||
//
|
||||
builder.put("clientIdentifier", convert(registrar.getRegistrarId()));
|
||||
builder.put("registrarId", convert(registrar.getRegistrarId()));
|
||||
builder.put("registrarName", convert(registrar.getRegistrarName()));
|
||||
builder.put("state", convert(registrar.getState()));
|
||||
builder.put("ianaIdentifier", convert(registrar.getIanaIdentifier()));
|
||||
|
||||
@@ -44,8 +44,8 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.flows.domain.DomainFlowUtils.BadCommandForRegistryPhaseException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.InvalidIdnDomainLabelException;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.label.ReservationType;
|
||||
import google.registry.monitoring.whitebox.CheckApiMetric;
|
||||
@@ -156,7 +156,7 @@ public class CheckApiAction implements Runnable {
|
||||
}
|
||||
|
||||
private boolean checkExists(String domainString, DateTime now) {
|
||||
return !ForeignKeyIndex.loadCached(Domain.class, ImmutableList.of(domainString), now).isEmpty();
|
||||
return !ForeignKeyUtils.loadCached(Domain.class, ImmutableList.of(domainString), now).isEmpty();
|
||||
}
|
||||
|
||||
private Optional<String> checkReserved(InternetDomainName domainName) {
|
||||
|
||||
@@ -17,7 +17,6 @@ package google.registry.flows;
|
||||
import static com.google.common.collect.Sets.intersection;
|
||||
import static google.registry.model.EppResourceUtils.isLinked;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.index.ForeignKeyIndex.loadAndGetKey;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -38,6 +37,7 @@ import google.registry.flows.exceptions.TooManyResourceChecksException;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.EppResource.ForeignKeyedEppResource;
|
||||
import google.registry.model.EppResource.ResourceWithTransferData;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
@@ -45,7 +45,6 @@ import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.List;
|
||||
@@ -70,22 +69,17 @@ public final class ResourceFlowUtils {
|
||||
/**
|
||||
* Check whether if there are domains linked to the resource to be deleted. Throws an exception if
|
||||
* so.
|
||||
*
|
||||
* <p>Note that in datastore this is a smoke test as the query for linked domains is eventually
|
||||
* consistent, so we only check a few domains to fail fast.
|
||||
*/
|
||||
public static <R extends EppResource> void checkLinkedDomains(
|
||||
final String targetId, final DateTime now, final Class<R> resourceClass) throws EppException {
|
||||
EppException failfastException =
|
||||
tm().transact(
|
||||
() -> {
|
||||
final ForeignKeyIndex<R> fki = ForeignKeyIndex.load(resourceClass, targetId, now);
|
||||
if (fki == null) {
|
||||
VKey<R> key = ForeignKeyUtils.load(resourceClass, targetId, now);
|
||||
if (key == null) {
|
||||
return new ResourceDoesNotExistException(resourceClass, targetId);
|
||||
}
|
||||
return isLinked(fki.getResourceKey(), now)
|
||||
? new ResourceToDeleteIsReferencedException()
|
||||
: null;
|
||||
return isLinked(key, now) ? new ResourceToDeleteIsReferencedException() : null;
|
||||
});
|
||||
if (failfastException != null) {
|
||||
throw failfastException;
|
||||
@@ -118,7 +112,7 @@ public final class ResourceFlowUtils {
|
||||
|
||||
public static <R extends EppResource> void verifyResourceDoesNotExist(
|
||||
Class<R> clazz, String targetId, DateTime now, String registrarId) throws EppException {
|
||||
VKey<R> key = loadAndGetKey(clazz, targetId, now);
|
||||
VKey<R> key = ForeignKeyUtils.load(clazz, targetId, now);
|
||||
if (key != null) {
|
||||
R resource = tm().loadByKey(key);
|
||||
// These are similar exceptions, but we can track them internally as log-based metrics.
|
||||
|
||||
@@ -53,6 +53,7 @@ import google.registry.flows.custom.DomainCheckFlowCustomLogic.BeforeResponseRet
|
||||
import google.registry.flows.domain.token.AllocationTokenDomainCheckResults;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainCommand.Check;
|
||||
@@ -70,7 +71,6 @@ import google.registry.model.eppoutput.CheckData.DomainCheck;
|
||||
import google.registry.model.eppoutput.CheckData.DomainCheckData;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldState;
|
||||
@@ -169,8 +169,8 @@ public final class DomainCheckFlow implements Flow {
|
||||
// TODO: Use as of date from fee extension v0.12 instead of now, if specified.
|
||||
.setAsOfDate(now)
|
||||
.build());
|
||||
ImmutableMap<String, ForeignKeyIndex<Domain>> existingDomains =
|
||||
ForeignKeyIndex.load(Domain.class, domainNames, now);
|
||||
ImmutableMap<String, VKey<Domain>> existingDomains =
|
||||
ForeignKeyUtils.load(Domain.class, domainNames, now);
|
||||
Optional<AllocationTokenExtension> allocationTokenExtension =
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class);
|
||||
Optional<AllocationTokenDomainCheckResults> tokenDomainCheckResults =
|
||||
@@ -227,7 +227,7 @@ public final class DomainCheckFlow implements Flow {
|
||||
|
||||
private Optional<String> getMessageForCheck(
|
||||
InternetDomainName domainName,
|
||||
ImmutableMap<String, ForeignKeyIndex<Domain>> existingDomains,
|
||||
ImmutableMap<String, VKey<Domain>> existingDomains,
|
||||
ImmutableMap<InternetDomainName, String> tokenCheckResults,
|
||||
ImmutableMap<String, TldState> tldStates,
|
||||
Optional<AllocationToken> allocationToken) {
|
||||
@@ -251,7 +251,7 @@ public final class DomainCheckFlow implements Flow {
|
||||
/** Handle the fee check extension. */
|
||||
private ImmutableList<? extends ResponseExtension> getResponseExtensions(
|
||||
ImmutableMap<String, InternetDomainName> domainNames,
|
||||
ImmutableMap<String, ForeignKeyIndex<Domain>> existingDomains,
|
||||
ImmutableMap<String, VKey<Domain>> existingDomains,
|
||||
ImmutableSet<String> availableDomains,
|
||||
DateTime now,
|
||||
Optional<AllocationToken> allocationToken)
|
||||
@@ -297,14 +297,14 @@ public final class DomainCheckFlow implements Flow {
|
||||
* renewal is part of the cost of a restore.
|
||||
*
|
||||
* <p>This may be resource-intensive for large checks of many restore fees, but those are
|
||||
* comparatively rare, and we are at least using an in-memory cache. Also this will get a lot
|
||||
* comparatively rare, and we are at least using an in-memory cache. Also, this will get a lot
|
||||
* nicer in Cloud SQL when we can SELECT just the fields we want rather than having to load the
|
||||
* entire entity.
|
||||
*/
|
||||
private ImmutableMap<String, Domain> loadDomainsForRestoreChecks(
|
||||
FeeCheckCommandExtension<?, ?> feeCheck,
|
||||
ImmutableMap<String, InternetDomainName> domainNames,
|
||||
ImmutableMap<String, ForeignKeyIndex<Domain>> existingDomains) {
|
||||
ImmutableMap<String, VKey<Domain>> existingDomains) {
|
||||
ImmutableList<String> restoreCheckDomains;
|
||||
if (feeCheck instanceof FeeCheckCommandExtensionV06) {
|
||||
// The V06 fee extension supports specifying the command fees to check on a per-domain basis.
|
||||
@@ -329,7 +329,7 @@ public final class DomainCheckFlow implements Flow {
|
||||
ImmutableMap<String, VKey<Domain>> existingDomainsToLoad =
|
||||
restoreCheckDomains.stream()
|
||||
.filter(existingDomains::containsKey)
|
||||
.collect(toImmutableMap(d -> d, d -> existingDomains.get(d).getResourceKey()));
|
||||
.collect(toImmutableMap(d -> d, existingDomains::get));
|
||||
ImmutableMap<VKey<? extends EppResource>, EppResource> loadedDomains =
|
||||
EppResource.loadCached(ImmutableList.copyOf(existingDomainsToLoad.values()));
|
||||
return ImmutableMap.copyOf(
|
||||
|
||||
@@ -248,7 +248,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
validateRegistrationPeriod(years);
|
||||
verifyResourceDoesNotExist(Domain.class, targetId, now, registrarId);
|
||||
// Validate that this is actually a legal domain name on a TLD that the registrar has access to.
|
||||
InternetDomainName domainName = validateDomainName(command.getFullyQualifiedDomainName());
|
||||
InternetDomainName domainName = validateDomainName(command.getDomainName());
|
||||
String domainLabel = domainName.parts().get(0);
|
||||
Registry registry = Registry.get(domainName.parent().toString());
|
||||
validateCreateCommandContactsAndNameservers(command, registry, domainName);
|
||||
@@ -639,10 +639,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
}
|
||||
|
||||
private static PollMessage.OneTime createNameCollisionOneTimePollMessage(
|
||||
String fullyQualifiedDomainName,
|
||||
HistoryEntry historyEntry,
|
||||
String registrarId,
|
||||
DateTime now) {
|
||||
String domainName, HistoryEntry historyEntry, String registrarId, DateTime now) {
|
||||
return new PollMessage.OneTime.Builder()
|
||||
.setRegistrarId(registrarId)
|
||||
.setEventTime(now)
|
||||
@@ -650,7 +647,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
.setResponseData(
|
||||
ImmutableList.of(
|
||||
DomainPendingActionNotificationResponse.create(
|
||||
fullyQualifiedDomainName, true, historyEntry.getTrid(), now)))
|
||||
domainName, true, historyEntry.getTrid(), now)))
|
||||
.setHistoryEntry(historyEntry)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ import google.registry.model.domain.launch.LaunchNotice;
|
||||
import google.registry.model.domain.launch.LaunchNotice.InvalidChecksumException;
|
||||
import google.registry.model.domain.launch.LaunchPhase;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.domain.secdns.DomainDsData;
|
||||
import google.registry.model.domain.secdns.SecDnsCreateExtension;
|
||||
import google.registry.model.domain.secdns.SecDnsInfoExtension;
|
||||
import google.registry.model.domain.secdns.SecDnsUpdateExtension;
|
||||
@@ -316,14 +316,14 @@ public class DomainFlowUtils {
|
||||
}
|
||||
|
||||
/** Check that the DS data that will be set on a domain is valid. */
|
||||
static void validateDsData(Set<DelegationSignerData> dsData) throws EppException {
|
||||
static void validateDsData(Set<DomainDsData> dsData) throws EppException {
|
||||
if (dsData != null) {
|
||||
if (dsData.size() > MAX_DS_RECORDS_PER_DOMAIN) {
|
||||
throw new TooManyDsRecordsException(
|
||||
String.format(
|
||||
"A maximum of %s DS records are allowed per domain.", MAX_DS_RECORDS_PER_DOMAIN));
|
||||
}
|
||||
ImmutableList<DelegationSignerData> invalidAlgorithms =
|
||||
ImmutableList<DomainDsData> invalidAlgorithms =
|
||||
dsData.stream()
|
||||
.filter(ds -> !validateAlgorithm(ds.getAlgorithm()))
|
||||
.collect(toImmutableList());
|
||||
@@ -333,7 +333,7 @@ public class DomainFlowUtils {
|
||||
"Domain contains DS record(s) with an invalid algorithm wire value: %s",
|
||||
invalidAlgorithms));
|
||||
}
|
||||
ImmutableList<DelegationSignerData> invalidDigestTypes =
|
||||
ImmutableList<DomainDsData> invalidDigestTypes =
|
||||
dsData.stream()
|
||||
.filter(ds -> !DigestType.fromWireValue(ds.getDigestType()).isPresent())
|
||||
.collect(toImmutableList());
|
||||
@@ -343,7 +343,7 @@ public class DomainFlowUtils {
|
||||
"Domain contains DS record(s) with an invalid digest type: %s",
|
||||
invalidDigestTypes));
|
||||
}
|
||||
ImmutableList<DelegationSignerData> digestsWithInvalidDigestLength =
|
||||
ImmutableList<DomainDsData> digestsWithInvalidDigestLength =
|
||||
dsData.stream()
|
||||
.filter(
|
||||
ds ->
|
||||
@@ -920,16 +920,15 @@ public class DomainFlowUtils {
|
||||
* and we are going to ignore it; clients who don't care about secDNS can just ignore it.
|
||||
*/
|
||||
static void addSecDnsExtensionIfPresent(
|
||||
ImmutableList.Builder<ResponseExtension> extensions,
|
||||
ImmutableSet<DelegationSignerData> dsData) {
|
||||
ImmutableList.Builder<ResponseExtension> extensions, ImmutableSet<DomainDsData> dsData) {
|
||||
if (!dsData.isEmpty()) {
|
||||
extensions.add(SecDnsInfoExtension.create(dsData));
|
||||
}
|
||||
}
|
||||
|
||||
/** Update {@link DelegationSignerData} based on an update extension command. */
|
||||
static ImmutableSet<DelegationSignerData> updateDsData(
|
||||
ImmutableSet<DelegationSignerData> oldDsData, SecDnsUpdateExtension secDnsUpdate)
|
||||
/** Update {@link DomainDsData} based on an update extension command. */
|
||||
static ImmutableSet<DomainDsData> updateDsData(
|
||||
ImmutableSet<DomainDsData> oldDsData, SecDnsUpdateExtension secDnsUpdate)
|
||||
throws EppException {
|
||||
// We don't support 'urgent' because we do everything as fast as we can anyways.
|
||||
if (Boolean.TRUE.equals(secDnsUpdate.getUrgent())) { // We allow both false and null.
|
||||
@@ -948,8 +947,8 @@ public class DomainFlowUtils {
|
||||
if (remove != null && Boolean.FALSE.equals(remove.getAll())) {
|
||||
throw new SecDnsAllUsageException(); // Explicit all=false is meaningless.
|
||||
}
|
||||
Set<DelegationSignerData> toAdd = (add == null) ? ImmutableSet.of() : add.getDsData();
|
||||
Set<DelegationSignerData> toRemove =
|
||||
Set<DomainDsData> toAdd = (add == null) ? ImmutableSet.of() : add.getDsData();
|
||||
Set<DomainDsData> toRemove =
|
||||
(remove == null)
|
||||
? ImmutableSet.of()
|
||||
: (remove.getAll() == null) ? remove.getDsData() : oldDsData;
|
||||
@@ -1001,9 +1000,9 @@ public class DomainFlowUtils {
|
||||
validateRegistrantAllowedOnTld(tld, command.getRegistrantContactId());
|
||||
validateNoDuplicateContacts(command.getContacts());
|
||||
validateRequiredContactsPresent(command.getRegistrant(), command.getContacts());
|
||||
ImmutableSet<String> fullyQualifiedHostNames = command.getNameserverFullyQualifiedHostNames();
|
||||
validateNameserversCountForTld(tld, domainName, fullyQualifiedHostNames.size());
|
||||
validateNameserversAllowedOnTld(tld, fullyQualifiedHostNames);
|
||||
ImmutableSet<String> hostNames = command.getNameserverHostNames();
|
||||
validateNameserversCountForTld(tld, domainName, hostNames.size());
|
||||
validateNameserversAllowedOnTld(tld, hostNames);
|
||||
}
|
||||
|
||||
/** Validate the secDNS extension, if present. */
|
||||
@@ -1542,11 +1541,11 @@ public class DomainFlowUtils {
|
||||
/** Nameservers are not allow-listed for this TLD. */
|
||||
public static class NameserversNotAllowedForTldException
|
||||
extends StatusProhibitsOperationException {
|
||||
public NameserversNotAllowedForTldException(Set<String> fullyQualifiedHostNames) {
|
||||
public NameserversNotAllowedForTldException(Set<String> hostNames) {
|
||||
super(
|
||||
String.format(
|
||||
"Nameservers '%s' are not allow-listed for this TLD",
|
||||
Joiner.on(',').join(fullyQualifiedHostNames)));
|
||||
Joiner.on(',').join(hostNames)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ public final class DomainInfoFlow implements Flow {
|
||||
// This is a policy decision that is left up to us by the rfcs.
|
||||
DomainInfoData.Builder infoBuilder =
|
||||
DomainInfoData.newBuilder()
|
||||
.setFullyQualifiedDomainName(domain.getDomainName())
|
||||
.setDomainName(domain.getDomainName())
|
||||
.setRepoId(domain.getRepoId())
|
||||
.setCurrentSponsorClientId(domain.getCurrentSponsorRegistrarId())
|
||||
.setRegistrant(
|
||||
|
||||
@@ -32,6 +32,8 @@ import static google.registry.flows.domain.DomainTransferUtils.createLosingTrans
|
||||
import static google.registry.flows.domain.DomainTransferUtils.createPendingTransferData;
|
||||
import static google.registry.flows.domain.DomainTransferUtils.createTransferResponse;
|
||||
import static google.registry.flows.domain.DomainTransferUtils.createTransferServerApproveEntities;
|
||||
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.maybeApplyPackageRemovalToken;
|
||||
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.verifyTokenAllowedOnDomain;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
@@ -63,6 +65,7 @@ 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.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationTokenExtension;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
@@ -132,6 +135,8 @@ import org.joda.time.DateTime;
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.MissingRemovePackageTokenOnPackageDomainException}
|
||||
*/
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_TRANSFER_REQUEST)
|
||||
public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
@@ -169,19 +174,24 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
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<AllocationToken> allocationToken =
|
||||
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
|
||||
existingDomain,
|
||||
Registry.get(existingDomain.getTld()),
|
||||
gainingClientId,
|
||||
now,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
Optional<DomainTransferRequestSuperuserExtension> superuserExtension =
|
||||
eppInput.getSingleExtension(DomainTransferRequestSuperuserExtension.class);
|
||||
Period period =
|
||||
superuserExtension.isPresent()
|
||||
? superuserExtension.get().getRenewalPeriod()
|
||||
: ((Transfer) resourceCommand).getPeriod();
|
||||
verifyTransferAllowed(existingDomain, period, now, superuserExtension);
|
||||
verifyTransferAllowed(existingDomain, period, now, superuserExtension, allocationToken);
|
||||
|
||||
// If client passed an applicable static token this updates the domain
|
||||
existingDomain = maybeApplyPackageRemovalToken(existingDomain, allocationToken);
|
||||
|
||||
String tld = existingDomain.getTld();
|
||||
Registry registry = Registry.get(tld);
|
||||
// An optional extension from the client specifying what they think the transfer should cost.
|
||||
@@ -293,9 +303,11 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
Domain existingDomain,
|
||||
Period period,
|
||||
DateTime now,
|
||||
Optional<DomainTransferRequestSuperuserExtension> superuserExtension)
|
||||
Optional<DomainTransferRequestSuperuserExtension> superuserExtension,
|
||||
Optional<AllocationToken> allocationToken)
|
||||
throws EppException {
|
||||
verifyNoDisallowedStatuses(existingDomain, DISALLOWED_STATUSES);
|
||||
verifyTokenAllowedOnDomain(existingDomain, allocationToken);
|
||||
if (!isSuperuser) {
|
||||
verifyAuthInfoPresentForResourceTransfer(authInfo);
|
||||
verifyAuthInfo(authInfo.get(), existingDomain);
|
||||
|
||||
@@ -218,7 +218,7 @@ public final class DomainTransferUtils {
|
||||
TransferData transferData,
|
||||
@Nullable DateTime extendedRegistrationExpirationTime) {
|
||||
return new DomainTransferResponse.Builder()
|
||||
.setFullyQualifiedDomainName(targetId)
|
||||
.setDomainName(targetId)
|
||||
.setGainingRegistrarId(transferData.getGainingRegistrarId())
|
||||
.setLosingRegistrarId(transferData.getLosingRegistrarId())
|
||||
.setPendingTransferExpirationTime(transferData.getPendingTransferExpirationTime())
|
||||
|
||||
@@ -74,7 +74,7 @@ import google.registry.model.domain.DomainCommand.Update.Change;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.fee.FeeUpdateCommandExtension;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.domain.secdns.DomainDsData;
|
||||
import google.registry.model.domain.secdns.SecDnsUpdateExtension;
|
||||
import google.registry.model.domain.superuser.DomainUpdateSuperuserExtension;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
@@ -87,6 +87,7 @@ import google.registry.model.poll.PendingActionNotificationResponse.DomainPendin
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Registry;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -181,7 +182,10 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
DomainHistory domainHistory =
|
||||
historyBuilder.setType(DOMAIN_UPDATE).setDomain(newDomain).build();
|
||||
validateNewState(newDomain);
|
||||
dnsQueue.addDomainRefreshTask(targetId);
|
||||
if (!Objects.equals(newDomain.getDsData(), existingDomain.getDsData())
|
||||
|| !Objects.equals(newDomain.getNsHosts(), existingDomain.getNsHosts())) {
|
||||
dnsQueue.addDomainRefreshTask(targetId);
|
||||
}
|
||||
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||
entitiesToSave.add(newDomain, domainHistory);
|
||||
Optional<BillingEvent.OneTime> statusUpdateBillingEvent =
|
||||
@@ -229,8 +233,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
validateContactsHaveTypes(add.getContacts());
|
||||
validateContactsHaveTypes(remove.getContacts());
|
||||
validateRegistrantAllowedOnTld(tld, command.getInnerChange().getRegistrantContactId());
|
||||
validateNameserversAllowedOnTld(
|
||||
tld, add.getNameserverFullyQualifiedHostNames());
|
||||
validateNameserversAllowedOnTld(tld, add.getNameserverHostNames());
|
||||
}
|
||||
|
||||
private Domain performUpdate(Update command, Domain domain, DateTime now) throws EppException {
|
||||
@@ -260,7 +263,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
secDnsUpdate.isPresent()
|
||||
? updateDsData(
|
||||
domain.getDsData().stream()
|
||||
.map(DelegationSignerData::cloneWithoutDomainRepoId)
|
||||
.map(DomainDsData::cloneWithoutDomainRepoId)
|
||||
.collect(toImmutableSet()),
|
||||
secDnsUpdate.get())
|
||||
: domain.getDsData())
|
||||
@@ -268,12 +271,18 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
.setLastEppUpdateRegistrarId(registrarId)
|
||||
.addStatusValues(add.getStatusValues())
|
||||
.removeStatusValues(remove.getStatusValues())
|
||||
.addNameservers(add.getNameservers().stream().collect(toImmutableSet()))
|
||||
.removeNameservers(remove.getNameservers().stream().collect(toImmutableSet()))
|
||||
.removeContacts(remove.getContacts())
|
||||
.addContacts(add.getContacts())
|
||||
.setRegistrant(firstNonNull(change.getRegistrant(), domain.getRegistrant()))
|
||||
.setAuthInfo(firstNonNull(change.getAuthInfo(), domain.getAuthInfo()));
|
||||
|
||||
if (!add.getNameservers().isEmpty()) {
|
||||
domainBuilder.addNameservers(add.getNameservers().stream().collect(toImmutableSet()));
|
||||
}
|
||||
if (!remove.getNameservers().isEmpty()) {
|
||||
domainBuilder.removeNameservers(remove.getNameservers().stream().collect(toImmutableSet()));
|
||||
}
|
||||
|
||||
Optional<DomainUpdateSuperuserExtension> superuserExt =
|
||||
eppInput.getSingleExtension(DomainUpdateSuperuserExtension.class);
|
||||
if (superuserExt.isPresent()) {
|
||||
|
||||
@@ -175,11 +175,7 @@ public class AllocationTokenFlowUtils {
|
||||
return Optional.empty();
|
||||
}
|
||||
AllocationToken tokenEntity = loadToken(extension.get().getAllocationToken());
|
||||
validateToken(
|
||||
InternetDomainName.from(command.getFullyQualifiedDomainName()),
|
||||
tokenEntity,
|
||||
registrarId,
|
||||
now);
|
||||
validateToken(InternetDomainName.from(command.getDomainName()), tokenEntity, registrarId, now);
|
||||
return Optional.of(
|
||||
tokenCustomLogic.validateToken(command, tokenEntity, registry, registrarId, now));
|
||||
}
|
||||
@@ -293,11 +289,11 @@ public class AllocationTokenFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** The __REMOVEPACKAGE__ token is missing on a renew package domain command */
|
||||
/** The __REMOVEPACKAGE__ token is missing on a package domain command */
|
||||
public static class MissingRemovePackageTokenOnPackageDomainException
|
||||
extends AssociationProhibitsOperationException {
|
||||
MissingRemovePackageTokenOnPackageDomainException() {
|
||||
super("Domains that are inside packages cannot be explicitly renewed");
|
||||
super("Domains that are inside packages cannot be explicitly renewed or transferred");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ public final class HostCreateFlow implements TransactionalFlow {
|
||||
superordinateDomain
|
||||
.get()
|
||||
.asBuilder()
|
||||
.addSubordinateHost(command.getFullyQualifiedHostName())
|
||||
.addSubordinateHost(command.getHostName())
|
||||
.build());
|
||||
// Only update DNS if this is a subordinate host. External hosts have no glue to write, so
|
||||
// they are only written as NS records from the referencing domain.
|
||||
|
||||
@@ -93,7 +93,7 @@ public final class HostInfoFlow implements Flow {
|
||||
return responseBuilder
|
||||
.setResData(
|
||||
hostInfoDataBuilder
|
||||
.setFullyQualifiedHostName(host.getHostName())
|
||||
.setHostName(host.getHostName())
|
||||
.setRepoId(host.getRepoId())
|
||||
.setStatusValues(statusValues.build())
|
||||
.setInetAddresses(host.getInetAddresses())
|
||||
|
||||
@@ -26,7 +26,6 @@ import static google.registry.flows.host.HostFlowUtils.lookupSuperordinateDomain
|
||||
import static google.registry.flows.host.HostFlowUtils.validateHostName;
|
||||
import static google.registry.flows.host.HostFlowUtils.verifySuperordinateDomainNotInPendingDelete;
|
||||
import static google.registry.flows.host.HostFlowUtils.verifySuperordinateDomainOwnership;
|
||||
import static google.registry.model.index.ForeignKeyIndex.loadAndGetKey;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.HOST_UPDATE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
@@ -46,6 +45,7 @@ import google.registry.flows.TransactionalFlow;
|
||||
import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
@@ -129,7 +129,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
extensionManager.validate();
|
||||
Update command = (Update) resourceCommand;
|
||||
Change change = command.getInnerChange();
|
||||
String suppliedNewHostName = change.getFullyQualifiedHostName();
|
||||
String suppliedNewHostName = change.getHostName();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
validateHostName(targetId);
|
||||
Host existingHost = loadAndVerifyExistence(Host.class, targetId, now);
|
||||
@@ -147,7 +147,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
EppResource owningResource = firstNonNull(oldSuperordinateDomain, existingHost);
|
||||
verifyUpdateAllowed(
|
||||
command, existingHost, newSuperordinateDomain.orElse(null), owningResource, isHostRename);
|
||||
if (isHostRename && loadAndGetKey(Host.class, newHostName, now) != null) {
|
||||
if (isHostRename && ForeignKeyUtils.load(Host.class, newHostName, now) != null) {
|
||||
throw new HostAlreadyExistsException(newHostName);
|
||||
}
|
||||
AddRemove add = command.getInnerAdd();
|
||||
@@ -260,7 +260,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
dnsQueue.addHostRefreshTask(existingHost.getHostName());
|
||||
}
|
||||
// In case of a rename, there are many updates we need to queue up.
|
||||
if (((Update) resourceCommand).getInnerChange().getFullyQualifiedHostName() != null) {
|
||||
if (((Update) resourceCommand).getInnerChange().getHostName() != null) {
|
||||
// If the renamed host is also subordinate, then we must enqueue an update to write the new
|
||||
// glue.
|
||||
if (newHost.isSubordinate()) {
|
||||
|
||||
@@ -140,10 +140,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
|
||||
DateTime lastEppUpdateTime;
|
||||
|
||||
/** Status values associated with this resource. */
|
||||
@Ignore
|
||||
@Column(name = "statuses")
|
||||
// TODO(b/177567432): rename to "statuses" once we're off datastore.
|
||||
Set<StatusValue> status;
|
||||
@Ignore Set<StatusValue> statuses;
|
||||
|
||||
public String getRepoId() {
|
||||
return repoId;
|
||||
@@ -189,7 +186,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
|
||||
}
|
||||
|
||||
public final ImmutableSet<StatusValue> getStatusValues() {
|
||||
return nullToEmptyImmutableCopy(status);
|
||||
return nullToEmptyImmutableCopy(statuses);
|
||||
}
|
||||
|
||||
public DateTime getDeletionTime() {
|
||||
@@ -307,7 +304,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
|
||||
statusValue,
|
||||
resourceClass.getSimpleName());
|
||||
}
|
||||
getInstance().status = statusValues;
|
||||
getInstance().statuses = statusValues;
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.HistoryEntryDao;
|
||||
import google.registry.model.tld.Registry;
|
||||
@@ -117,20 +116,19 @@ public final class EppResourceUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the last created version of an {@link EppResource} from Datastore by foreign key, using a
|
||||
* cache.
|
||||
* Loads the last created version of an {@link EppResource} from the database by foreign key,
|
||||
* using a cache.
|
||||
*
|
||||
* <p>Returns null if no resource with this foreign key was ever created, or if the most recently
|
||||
* created resource was deleted before time "now".
|
||||
*
|
||||
* <p>Loading an {@link EppResource} by itself is not sufficient to know its current state since
|
||||
* it may have various expirable conditions and status values that might implicitly change its
|
||||
* state as time progresses even if it has not been updated in Datastore. Rather, the resource
|
||||
* state as time progresses even if it has not been updated in the database. Rather, the resource
|
||||
* must be combined with a timestamp to view its current state. We use a global last updated
|
||||
* timestamp on the resource's entity group (which is essentially free since all writes to the
|
||||
* entity group must be serialized anyways) to guarantee monotonically increasing write times, and
|
||||
* forward our projected time to the greater of this timestamp or "now". This guarantees that
|
||||
* we're not projecting into the past.
|
||||
* timestamp to guarantee monotonically increasing write times, and forward our projected time to
|
||||
* the greater of this timestamp or "now". This guarantees that we're not projecting into the
|
||||
* past.
|
||||
*
|
||||
* <p>Do not call this cached version for anything that needs transactional consistency. It should
|
||||
* only be used when it's OK if the data is potentially being out of date, e.g. WHOIS.
|
||||
@@ -150,19 +148,18 @@ public final class EppResourceUtils {
|
||||
checkArgument(
|
||||
ForeignKeyedEppResource.class.isAssignableFrom(clazz),
|
||||
"loadByForeignKey may only be called for foreign keyed EPP resources");
|
||||
ForeignKeyIndex<T> fki =
|
||||
VKey<T> key =
|
||||
useCache
|
||||
? ForeignKeyIndex.loadCached(clazz, ImmutableList.of(foreignKey), now)
|
||||
.getOrDefault(foreignKey, null)
|
||||
: ForeignKeyIndex.load(clazz, foreignKey, now);
|
||||
// The value of fki.getResourceKey() might be null for hard-deleted prober data.
|
||||
if (fki == null || isAtOrAfter(now, fki.getDeletionTime()) || fki.getResourceKey() == null) {
|
||||
? ForeignKeyUtils.loadCached(clazz, ImmutableList.of(foreignKey), now).get(foreignKey)
|
||||
: ForeignKeyUtils.load(clazz, foreignKey, now);
|
||||
// The returned key is null if the resource is hard deleted or soft deleted by the given time.
|
||||
if (key == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
T resource =
|
||||
useCache
|
||||
? EppResource.loadCached(fki.getResourceKey())
|
||||
: tm().transact(() -> tm().loadByKeyIfPresent(fki.getResourceKey()).orElse(null));
|
||||
? EppResource.loadCached(key)
|
||||
: tm().transact(() -> tm().loadByKeyIfPresent(key).orElse(null));
|
||||
if (resource == null || isAtOrAfter(now, resource.getDeletionTime())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -178,7 +175,7 @@ public final class EppResourceUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks multiple {@link EppResource} objects from Datastore by unique ids.
|
||||
* Checks multiple {@link EppResource} objects from the database by unique ids.
|
||||
*
|
||||
* <p>There are currently no resources that support checks and do not use foreign keys. If we need
|
||||
* to support that case in the future, we can loosen the type to allow any {@link EppResource} and
|
||||
@@ -190,7 +187,7 @@ public final class EppResourceUtils {
|
||||
*/
|
||||
public static <T extends EppResource> ImmutableSet<String> checkResourcesExist(
|
||||
Class<T> clazz, List<String> uniqueIds, final DateTime now) {
|
||||
return ForeignKeyIndex.load(clazz, uniqueIds, now).keySet();
|
||||
return ForeignKeyUtils.load(clazz, uniqueIds, now).keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
// 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;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static google.registry.config.RegistryConfig.getEppResourceCachingDuration;
|
||||
import static google.registry.config.RegistryConfig.getEppResourceMaxCachedEntries;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaJpaTm;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Streams;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Util class to map a foreign key to the {@link VKey} to the active instance of {@link EppResource}
|
||||
* whose unique repoId matches the foreign key string at a given time. The instance is never
|
||||
* deleted, but it is updated if a newer entity becomes the active entity.
|
||||
*/
|
||||
public final class ForeignKeyUtils {
|
||||
|
||||
private ForeignKeyUtils() {}
|
||||
|
||||
private static final ImmutableMap<Class<? extends EppResource>, String>
|
||||
RESOURCE_TYPE_TO_FK_PROPERTY =
|
||||
ImmutableMap.of(
|
||||
Contact.class, "contactId",
|
||||
Domain.class, "domainName",
|
||||
Host.class, "hostName");
|
||||
|
||||
/**
|
||||
* Loads a {@link VKey} to an {@link EppResource} from the database by foreign key.
|
||||
*
|
||||
* <p>Returns null if no resource with this foreign key was ever created, or if the most recently
|
||||
* created resource was deleted before time "now".
|
||||
*
|
||||
* @param clazz the resource type to load
|
||||
* @param foreignKey foreign key to match
|
||||
* @param now the current logical time to use when checking for soft deletion of the foreign key
|
||||
* index
|
||||
*/
|
||||
@Nullable
|
||||
public static <E extends EppResource> VKey<E> load(
|
||||
Class<E> clazz, String foreignKey, DateTime now) {
|
||||
return load(clazz, ImmutableList.of(foreignKey), now).get(foreignKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a map of {@link String} foreign keys to {@link VKey}s to {@link EppResource} that are
|
||||
* active at or after the specified moment in time.
|
||||
*
|
||||
* <p>The returned map will omit any foreign keys for which the {@link EppResource} doesn't exist
|
||||
* or has been soft deleted.
|
||||
*/
|
||||
public static <E extends EppResource> ImmutableMap<String, VKey<E>> load(
|
||||
Class<E> clazz, Collection<String> foreignKeys, final DateTime now) {
|
||||
return load(clazz, foreignKeys, false).entrySet().stream()
|
||||
.filter(e -> now.isBefore(e.getValue().deletionTime()))
|
||||
.collect(toImmutableMap(Entry::getKey, e -> VKey.createSql(clazz, e.getValue().repoId())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to load {@link VKey}s to all the most recent {@link EppResource}s for the given
|
||||
* foreign keys, regardless of whether or not they have been soft-deleted.
|
||||
*
|
||||
* <p>Used by both the cached (w/o deletion check) and the non-cached (with deletion check) calls.
|
||||
*
|
||||
* <p>Note that in production, the {@code deletionTime} for entities with the same foreign key
|
||||
* should monotonically increase as one cannot create a domain/host/contact with the same foreign
|
||||
* key without soft deleting the existing resource first. However, in test, there's no such
|
||||
* guarantee and one must make sure that no two resources with the same foreign key exist with the
|
||||
* same max {@code deleteTime}, usually {@code END_OF_TIME}, lest this method throws an error due
|
||||
* to duplicate keys.
|
||||
*/
|
||||
private static <E extends EppResource> ImmutableMap<String, MostRecentResource> load(
|
||||
Class<E> clazz, Collection<String> foreignKeys, boolean useReplicaJpaTm) {
|
||||
String fkProperty = RESOURCE_TYPE_TO_FK_PROPERTY.get(clazz);
|
||||
JpaTransactionManager jpaTmToUse = useReplicaJpaTm ? replicaJpaTm() : jpaTm();
|
||||
return jpaTmToUse.transact(
|
||||
() ->
|
||||
jpaTmToUse
|
||||
.query(
|
||||
("SELECT %fkProperty%, repoId, deletionTime FROM %entity% WHERE (%fkProperty%,"
|
||||
+ " deletionTime) IN (SELECT %fkProperty%, MAX(deletionTime) FROM"
|
||||
+ " %entity% WHERE %fkProperty% IN (:fks) GROUP BY %fkProperty%)")
|
||||
.replace("%fkProperty%", fkProperty)
|
||||
.replace("%entity%", clazz.getSimpleName()),
|
||||
Object[].class)
|
||||
.setParameter("fks", foreignKeys)
|
||||
.getResultStream()
|
||||
.collect(
|
||||
toImmutableMap(
|
||||
row -> (String) row[0],
|
||||
row -> MostRecentResource.create((String) row[1], (DateTime) row[2]))));
|
||||
}
|
||||
|
||||
private static final CacheLoader<VKey<? extends EppResource>, Optional<MostRecentResource>>
|
||||
CACHE_LOADER =
|
||||
new CacheLoader<VKey<? extends EppResource>, Optional<MostRecentResource>>() {
|
||||
|
||||
@Override
|
||||
public Optional<MostRecentResource> load(VKey<? extends EppResource> key) {
|
||||
return loadAll(ImmutableList.of(key)).get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<VKey<? extends EppResource>, Optional<MostRecentResource>> loadAll(
|
||||
Iterable<? extends VKey<? extends EppResource>> keys) {
|
||||
if (!keys.iterator().hasNext()) {
|
||||
return ImmutableMap.of();
|
||||
}
|
||||
// It is safe to use the resource type of first element because when this function is
|
||||
// called, it is always passed with a list of VKeys with the same type.
|
||||
Class<? extends EppResource> clazz = keys.iterator().next().getKind();
|
||||
ImmutableList<String> foreignKeys =
|
||||
Streams.stream(keys)
|
||||
.map(key -> (String) key.getSqlKey())
|
||||
.collect(toImmutableList());
|
||||
ImmutableMap<String, MostRecentResource> existingKeys =
|
||||
ForeignKeyUtils.load(clazz, foreignKeys, true);
|
||||
// The above map only contains keys that exist in the database, so we re-add the
|
||||
// missing ones with Optional.empty() values for caching.
|
||||
return Maps.asMap(
|
||||
ImmutableSet.copyOf(keys),
|
||||
key -> Optional.ofNullable(existingKeys.get((String) key.getSqlKey())));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A limited size, limited time cache for foreign-keyed entities.
|
||||
*
|
||||
* <p>This is only used to cache foreign-keyed entities for the purposes of checking whether they
|
||||
* exist (and if so, what entity they point to) during a few domain flows. Any other operations on
|
||||
* foreign keys should not use this cache.
|
||||
*
|
||||
* <p>Note that here the key of the {@link LoadingCache} is of type {@code VKey<? extends
|
||||
* EppResource>}, but they are not legal {VKey}s to {@link EppResource}s, whose keys are the SQL
|
||||
* primary keys, i.e. the {@code repoId}s. Instead, their keys are the foreign keys used to query
|
||||
* the database. We use {@link VKey} here because it is a convenient composite class that contains
|
||||
* both the resource type and the foreign key, which are needed to for the query and caching.
|
||||
*
|
||||
* <p>Also note that the value type of this cache is {@link Optional} because the foreign keys in
|
||||
* question are coming from external commands, and thus don't necessarily represent entities in
|
||||
* our system that actually exist. So we cache the fact that they *don't* exist by using
|
||||
* Optional.empty(), then several layers up the EPP command will fail with an error message like
|
||||
* "The contact with given IDs (blah) don't exist."
|
||||
*/
|
||||
@NonFinalForTesting
|
||||
private static LoadingCache<VKey<? extends EppResource>, Optional<MostRecentResource>>
|
||||
foreignKeyCache = createForeignKeyMapCache(getEppResourceCachingDuration());
|
||||
|
||||
private static LoadingCache<VKey<? extends EppResource>, Optional<MostRecentResource>>
|
||||
createForeignKeyMapCache(Duration expiry) {
|
||||
return CacheUtils.newCacheBuilder(expiry)
|
||||
.maximumSize(getEppResourceMaxCachedEntries())
|
||||
.build(CACHE_LOADER);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setCacheForTest(Optional<Duration> expiry) {
|
||||
Duration effectiveExpiry = expiry.orElse(getEppResourceCachingDuration());
|
||||
foreignKeyCache = createForeignKeyMapCache(effectiveExpiry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a list of {@link VKey} to {@link EppResource} instances by class and foreign key strings
|
||||
* that are active at or after the specified moment in time, using the cache if enabled.
|
||||
*
|
||||
* <p>The returned map will omit any keys for which the {@link EppResource} doesn't exist or has
|
||||
* been soft deleted.
|
||||
*
|
||||
* <p>Don't use the cached version of this method unless you really need it for performance
|
||||
* reasons, and are OK with the trade-offs in loss of transactional consistency.
|
||||
*/
|
||||
public static <E extends EppResource> ImmutableMap<String, VKey<E>> loadCached(
|
||||
Class<E> clazz, Collection<String> foreignKeys, final DateTime now) {
|
||||
if (!RegistryConfig.isEppResourceCachingEnabled()) {
|
||||
return load(clazz, foreignKeys, now);
|
||||
}
|
||||
return foreignKeyCache
|
||||
.getAll(
|
||||
foreignKeys.stream().map(fk -> VKey.createSql(clazz, fk)).collect(toImmutableList()))
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(e -> e.getValue().isPresent() && now.isBefore(e.getValue().get().deletionTime()))
|
||||
.collect(
|
||||
toImmutableMap(
|
||||
e -> (String) e.getKey().getSqlKey(),
|
||||
e -> VKey.createSql(clazz, e.getValue().get().repoId())));
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
abstract static class MostRecentResource {
|
||||
|
||||
abstract String repoId();
|
||||
|
||||
abstract DateTime deletionTime();
|
||||
|
||||
static MostRecentResource create(String repoId, DateTime deletionTime) {
|
||||
return new AutoValue_ForeignKeyUtils_MostRecentResource(repoId, deletionTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ public class OteStats {
|
||||
((DomainCommand.Create)
|
||||
((ResourceCommandWrapper) eppInput.getCommandWrapper().getCommand())
|
||||
.getResourceCommand())
|
||||
.getFullyQualifiedDomainName()
|
||||
.getDomainName()
|
||||
.startsWith(ACE_PREFIX);
|
||||
|
||||
private static final Predicate<EppInput> IS_SUBORDINATE =
|
||||
|
||||
@@ -64,7 +64,7 @@ public final class ResourceTransferUtils {
|
||||
DomainTransferData domainTransferData = (DomainTransferData) transferData;
|
||||
builder =
|
||||
new DomainTransferResponse.Builder()
|
||||
.setFullyQualifiedDomainName(eppResource.getForeignKey())
|
||||
.setDomainName(eppResource.getForeignKey())
|
||||
.setExtendedRegistrationExpirationTime(
|
||||
ADD_EXDATE_STATUSES.contains(domainTransferData.getTransferStatus())
|
||||
? domainTransferData.getTransferredRegistrationExpirationTime()
|
||||
|
||||
@@ -24,7 +24,7 @@ import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
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.DomainDsData;
|
||||
import google.registry.model.domain.secdns.DomainDsDataHistory;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
@@ -67,12 +67,12 @@ public class BulkQueryEntities {
|
||||
public static Domain assembleDomain(
|
||||
DomainLite domainLite,
|
||||
ImmutableSet<GracePeriod> gracePeriods,
|
||||
ImmutableSet<DelegationSignerData> delegationSignerData,
|
||||
ImmutableSet<DomainDsData> domainDsData,
|
||||
ImmutableSet<VKey<Host>> nsHosts) {
|
||||
Domain.Builder builder = new Domain.Builder();
|
||||
builder.copyFrom(domainLite);
|
||||
builder.setGracePeriods(gracePeriods);
|
||||
builder.setDsData(delegationSignerData);
|
||||
builder.setDsData(domainDsData);
|
||||
builder.setNameservers(nsHosts);
|
||||
// Restore the original update timestamp (this gets cleared when we set nameservers or DS data).
|
||||
builder.setUpdateTimestamp(domainLite.getUpdateTimestamp());
|
||||
@@ -99,9 +99,7 @@ public class BulkQueryEntities {
|
||||
.map(GracePeriod::createFromHistory)
|
||||
.collect(toImmutableSet()))
|
||||
.setDsData(
|
||||
dsDataHistories.stream()
|
||||
.map(DelegationSignerData::create)
|
||||
.collect(toImmutableSet()))
|
||||
dsDataHistories.stream().map(DomainDsData::create).collect(toImmutableSet()))
|
||||
// Restore the original update timestamp (this gets cleared when we set nameservers or
|
||||
// DS data).
|
||||
.setUpdateTimestamp(domainHistoryLite.domainBase.getUpdateTimestamp())
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package google.registry.model.contact;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.EppResource.ForeignKeyedEppResource;
|
||||
import google.registry.model.annotations.ExternalMessagingName;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
@@ -46,13 +45,13 @@ import org.joda.time.DateTime;
|
||||
@Index(columnList = "searchName")
|
||||
})
|
||||
@ExternalMessagingName("contact")
|
||||
@WithStringVKey
|
||||
@WithStringVKey(compositeKey = true)
|
||||
@Access(AccessType.FIELD)
|
||||
public class Contact extends ContactBase implements ForeignKeyedEppResource {
|
||||
|
||||
@Override
|
||||
public VKey<Contact> createVKey() {
|
||||
return VKey.create(Contact.class, getRepoId(), Key.create(this));
|
||||
return VKey.createSql(Contact.class, getRepoId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,7 +19,7 @@ import google.registry.model.EppResource;
|
||||
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.domain.secdns.DomainDsData;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithStringVKey;
|
||||
@@ -66,7 +66,7 @@ import org.joda.time.DateTime;
|
||||
@Index(columnList = "transfer_billing_event_id"),
|
||||
@Index(columnList = "transfer_billing_recurrence_id")
|
||||
})
|
||||
@WithStringVKey
|
||||
@WithStringVKey(compositeKey = true)
|
||||
@ExternalMessagingName("domain")
|
||||
@Access(AccessType.FIELD)
|
||||
public class Domain extends DomainBase implements ForeignKeyedEppResource {
|
||||
@@ -116,15 +116,14 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of {@link DelegationSignerData} associated with the domain.
|
||||
* Returns the set of {@link DomainDsData} associated with the domain.
|
||||
*
|
||||
* <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
|
||||
* Hibernate would try to set the foreign key to null(through an UPDATE TABLE sql) instead of
|
||||
* deleting the whole entry from the table when the {@link DelegationSignerData} is removed from
|
||||
* the set.
|
||||
* deleting the whole entry from the table when the {@link DomainDsData} is removed from the set.
|
||||
*/
|
||||
@Access(AccessType.PROPERTY)
|
||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
|
||||
@@ -134,7 +133,7 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
|
||||
insertable = false,
|
||||
updatable = false)
|
||||
@SuppressWarnings("UnusedMethod")
|
||||
private Set<DelegationSignerData> getInternalDelegationSignerData() {
|
||||
private Set<DomainDsData> getInternalDelegationSignerData() {
|
||||
return dsData;
|
||||
}
|
||||
|
||||
@@ -148,7 +147,7 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
|
||||
|
||||
@Override
|
||||
public VKey<Domain> createVKey() {
|
||||
return VKey.create(Domain.class, getRepoId(), Key.create(this));
|
||||
return VKey.createSql(Domain.class, getRepoId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -53,7 +53,7 @@ import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.contact.Contact;
|
||||
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.secdns.DomainDsData;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.Host;
|
||||
@@ -112,17 +112,14 @@ public class DomainBase extends EppResource
|
||||
* Fully qualified domain name (puny-coded), which serves as the foreign key for this domain.
|
||||
*
|
||||
* <p>This is only unique in the sense that for any given lifetime specified as the time range
|
||||
* from (creationTime, deletionTime) there can only be one domain in Datastore with this name.
|
||||
* from (creationTime, deletionTime) there can only be one domain in the database with this name.
|
||||
* However, there can be many domains with the same name and non-overlapping lifetimes.
|
||||
*
|
||||
* @invariant fullyQualifiedDomainName == fullyQualifiedDomainName.toLowerCase(Locale.ENGLISH)
|
||||
* @invariant domainName == domainName.toLowerCase(Locale.ENGLISH)
|
||||
*/
|
||||
// TODO(b/177567432): Rename this to domainName when we are off Datastore
|
||||
@Column(name = "domainName")
|
||||
@Index
|
||||
String fullyQualifiedDomainName;
|
||||
@Index String domainName;
|
||||
|
||||
/** The top level domain this is under, dernormalized from {@link #fullyQualifiedDomainName}. */
|
||||
/** The top level domain this is under, dernormalized from {@link #domainName}. */
|
||||
@Index String tld;
|
||||
|
||||
/** References to hosts that are the nameservers for the domain. */
|
||||
@@ -145,7 +142,7 @@ public class DomainBase extends EppResource
|
||||
DomainAuthInfo authInfo;
|
||||
|
||||
/** Data used to construct DS records for this domain. */
|
||||
@Ignore @Transient Set<DelegationSignerData> dsData;
|
||||
@Ignore @Transient Set<DomainDsData> dsData;
|
||||
|
||||
/**
|
||||
* The claims notice supplied when this domain was created, if there was one.
|
||||
@@ -340,14 +337,14 @@ public class DomainBase extends EppResource
|
||||
|
||||
@Override
|
||||
public String getForeignKey() {
|
||||
return fullyQualifiedDomainName;
|
||||
return domainName;
|
||||
}
|
||||
|
||||
public String getDomainName() {
|
||||
return fullyQualifiedDomainName;
|
||||
return domainName;
|
||||
}
|
||||
|
||||
public ImmutableSet<DelegationSignerData> getDsData() {
|
||||
public ImmutableSet<DomainDsData> getDsData() {
|
||||
return nullToEmptyImmutableCopy(dsData);
|
||||
}
|
||||
|
||||
@@ -392,9 +389,9 @@ public class DomainBase extends EppResource
|
||||
|
||||
// Hibernate needs this in order to populate dsData but no one else should ever use it
|
||||
@SuppressWarnings("UnusedMethod")
|
||||
private void setInternalDelegationSignerData(Set<DelegationSignerData> dsData) {
|
||||
private void setInternalDelegationSignerData(Set<DomainDsData> dsData) {
|
||||
if (this.dsData instanceof PersistentSet) {
|
||||
Set<DelegationSignerData> nonNullDsData = nullToEmpty(dsData);
|
||||
Set<DomainDsData> nonNullDsData = nullToEmpty(dsData);
|
||||
this.dsData.retainAll(nonNullDsData);
|
||||
this.dsData.addAll(nonNullDsData);
|
||||
} else {
|
||||
@@ -728,9 +725,9 @@ public class DomainBase extends EppResource
|
||||
// If there is no autorenew end time, set it to END_OF_TIME.
|
||||
instance.autorenewEndTime = firstNonNull(getInstance().autorenewEndTime, END_OF_TIME);
|
||||
|
||||
checkArgumentNotNull(emptyToNull(instance.fullyQualifiedDomainName), "Missing domainName");
|
||||
checkArgumentNotNull(emptyToNull(instance.domainName), "Missing domainName");
|
||||
checkArgumentNotNull(instance.getRegistrant(), "Missing registrant");
|
||||
instance.tld = getTldFromDomainName(instance.fullyQualifiedDomainName);
|
||||
instance.tld = getTldFromDomainName(instance.domainName);
|
||||
|
||||
T newDomain = super.build();
|
||||
// Hibernate throws exception if gracePeriods or dsData is null because we enabled all
|
||||
@@ -751,11 +748,11 @@ public class DomainBase extends EppResource
|
||||
domainName.equals(canonicalizeHostname(domainName)),
|
||||
"Domain name %s not in puny-coded, lower-case form",
|
||||
domainName);
|
||||
getInstance().fullyQualifiedDomainName = domainName;
|
||||
getInstance().domainName = domainName;
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setDsData(ImmutableSet<DelegationSignerData> dsData) {
|
||||
public B setDsData(ImmutableSet<DomainDsData> dsData) {
|
||||
getInstance().dsData = dsData;
|
||||
getInstance().resetUpdateTimestamp();
|
||||
return thisCastToDerived();
|
||||
|
||||
@@ -17,7 +17,6 @@ package google.registry.model.domain;
|
||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||
import static com.google.common.collect.Maps.transformValues;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static google.registry.util.CollectionUtils.difference;
|
||||
import static google.registry.util.CollectionUtils.forceEmptyToNull;
|
||||
@@ -31,6 +30,7 @@ import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.eppinput.ResourceCommand.AbstractSingleResourceCommand;
|
||||
@@ -39,7 +39,6 @@ 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.Host;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -106,9 +105,9 @@ public class DomainCommand {
|
||||
@XmlRootElement
|
||||
@XmlType(
|
||||
propOrder = {
|
||||
"fullyQualifiedDomainName",
|
||||
"domainName",
|
||||
"period",
|
||||
"nameserverFullyQualifiedHostNames",
|
||||
"nameserverHostNames",
|
||||
"registrantContactId",
|
||||
"foreignKeyedDesignatedContacts",
|
||||
"authInfo"
|
||||
@@ -118,12 +117,12 @@ public class DomainCommand {
|
||||
|
||||
/** Fully qualified domain name, which serves as a unique identifier for this domain. */
|
||||
@XmlElement(name = "name")
|
||||
String fullyQualifiedDomainName;
|
||||
String domainName;
|
||||
|
||||
/** Fully qualified host names of the hosts that are the nameservers for the domain. */
|
||||
@XmlElementWrapper(name = "ns")
|
||||
@XmlElement(name = "hostObj")
|
||||
Set<String> nameserverFullyQualifiedHostNames;
|
||||
Set<String> nameserverHostNames;
|
||||
|
||||
/** Resolved keys to hosts that are the nameservers for the domain. */
|
||||
@XmlTransient Set<VKey<Host>> nameservers;
|
||||
@@ -145,15 +144,15 @@ public class DomainCommand {
|
||||
|
||||
@Override
|
||||
public String getTargetId() {
|
||||
return fullyQualifiedDomainName;
|
||||
return domainName;
|
||||
}
|
||||
|
||||
public String getFullyQualifiedDomainName() {
|
||||
return fullyQualifiedDomainName;
|
||||
public String getDomainName() {
|
||||
return domainName;
|
||||
}
|
||||
|
||||
public ImmutableSet<String> getNameserverFullyQualifiedHostNames() {
|
||||
return nullToEmptyImmutableCopy(nameserverFullyQualifiedHostNames);
|
||||
public ImmutableSet<String> getNameserverHostNames() {
|
||||
return nullToEmptyImmutableCopy(nameserverHostNames);
|
||||
}
|
||||
|
||||
public ImmutableSet<VKey<Host>> getNameservers() {
|
||||
@@ -173,7 +172,7 @@ public class DomainCommand {
|
||||
@Override
|
||||
public Create cloneAndLinkReferences(DateTime now) throws InvalidReferencesException {
|
||||
Create clone = clone(this);
|
||||
clone.nameservers = linkHosts(clone.nameserverFullyQualifiedHostNames, now);
|
||||
clone.nameservers = linkHosts(clone.nameserverHostNames, now);
|
||||
if (registrantContactId == null) {
|
||||
clone.contacts = linkContacts(clone.foreignKeyedDesignatedContacts, now);
|
||||
} else {
|
||||
@@ -206,7 +205,7 @@ public class DomainCommand {
|
||||
|
||||
/** The name of the domain to look up, and an attribute specifying the host lookup type. */
|
||||
@XmlElement(name = "name")
|
||||
NameWithHosts fullyQualifiedDomainName;
|
||||
NameWithHosts domainName;
|
||||
|
||||
DomainAuthInfo authInfo;
|
||||
|
||||
@@ -245,12 +244,12 @@ public class DomainCommand {
|
||||
/** Get the enum that specifies the requested hosts (applies only to info flows). */
|
||||
public HostsRequest getHostsRequest() {
|
||||
// Null "hosts" is implicitly ALL.
|
||||
return MoreObjects.firstNonNull(fullyQualifiedDomainName.hosts, HostsRequest.ALL);
|
||||
return MoreObjects.firstNonNull(domainName.hosts, HostsRequest.ALL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTargetId() {
|
||||
return fullyQualifiedDomainName.name;
|
||||
return domainName.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -338,15 +337,12 @@ public class DomainCommand {
|
||||
}
|
||||
|
||||
/** The inner change type on a domain update command. */
|
||||
@XmlType(propOrder = {
|
||||
"nameserverFullyQualifiedHostNames",
|
||||
"foreignKeyedDesignatedContacts",
|
||||
"statusValues"})
|
||||
@XmlType(propOrder = {"nameserverHostNames", "foreignKeyedDesignatedContacts", "statusValues"})
|
||||
public static class AddRemove extends ResourceUpdate.AddRemove {
|
||||
/** Fully qualified host names of the hosts that are the nameservers for the domain. */
|
||||
@XmlElementWrapper(name = "ns")
|
||||
@XmlElement(name = "hostObj")
|
||||
Set<String> nameserverFullyQualifiedHostNames;
|
||||
Set<String> nameserverHostNames;
|
||||
|
||||
/** Resolved keys to hosts that are the nameservers for the domain. */
|
||||
@XmlTransient Set<VKey<Host>> nameservers;
|
||||
@@ -359,8 +355,8 @@ public class DomainCommand {
|
||||
@XmlTransient
|
||||
Set<DesignatedContact> contacts;
|
||||
|
||||
public ImmutableSet<String> getNameserverFullyQualifiedHostNames() {
|
||||
return nullSafeImmutableCopy(nameserverFullyQualifiedHostNames);
|
||||
public ImmutableSet<String> getNameserverHostNames() {
|
||||
return nullSafeImmutableCopy(nameserverHostNames);
|
||||
}
|
||||
|
||||
public ImmutableSet<VKey<Host>> getNameservers() {
|
||||
@@ -374,7 +370,7 @@ public class DomainCommand {
|
||||
/** Creates a copy of this {@link AddRemove} with hard links to hosts and contacts. */
|
||||
private AddRemove cloneAndLinkReferences(DateTime now) throws InvalidReferencesException {
|
||||
AddRemove clone = clone(this);
|
||||
clone.nameservers = linkHosts(clone.nameserverFullyQualifiedHostNames, now);
|
||||
clone.nameservers = linkHosts(clone.nameserverHostNames, now);
|
||||
clone.contacts = linkContacts(clone.foreignKeyedDesignatedContacts, now);
|
||||
return clone;
|
||||
}
|
||||
@@ -413,13 +409,12 @@ public class DomainCommand {
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<VKey<Host>> linkHosts(Set<String> fullyQualifiedHostNames, DateTime now)
|
||||
private static Set<VKey<Host>> linkHosts(Set<String> hostNames, DateTime now)
|
||||
throws InvalidReferencesException {
|
||||
if (fullyQualifiedHostNames == null) {
|
||||
if (hostNames == null) {
|
||||
return null;
|
||||
}
|
||||
return ImmutableSet.copyOf(
|
||||
loadByForeignKeysCached(fullyQualifiedHostNames, Host.class, now).values());
|
||||
return ImmutableSet.copyOf(loadByForeignKeysCached(hostNames, Host.class, now).values());
|
||||
}
|
||||
|
||||
private static Set<DesignatedContact> linkContacts(
|
||||
@@ -445,13 +440,12 @@ public class DomainCommand {
|
||||
private static <T extends EppResource> ImmutableMap<String, VKey<T>> loadByForeignKeysCached(
|
||||
final Set<String> foreignKeys, final Class<T> clazz, final DateTime now)
|
||||
throws InvalidReferencesException {
|
||||
ImmutableMap<String, ForeignKeyIndex<T>> fkis =
|
||||
ForeignKeyIndex.loadCached(clazz, foreignKeys, now);
|
||||
if (!fkis.keySet().equals(foreignKeys)) {
|
||||
ImmutableMap<String, VKey<T>> fks = ForeignKeyUtils.loadCached(clazz, foreignKeys, now);
|
||||
if (!fks.keySet().equals(foreignKeys)) {
|
||||
throw new InvalidReferencesException(
|
||||
clazz, ImmutableSet.copyOf(difference(foreignKeys, fkis.keySet())));
|
||||
clazz, ImmutableSet.copyOf(difference(foreignKeys, fks.keySet())));
|
||||
}
|
||||
return ImmutableMap.copyOf(transformValues(fkis, ForeignKeyIndex::getResourceKey));
|
||||
return fks;
|
||||
}
|
||||
|
||||
/** Exception to throw when referenced objects don't exist. */
|
||||
|
||||
@@ -25,7 +25,7 @@ import google.registry.model.EppResource;
|
||||
import google.registry.model.ImmutableObject;
|
||||
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.DomainDsData;
|
||||
import google.registry.model.domain.secdns.DomainDsDataHistory;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
@@ -280,7 +280,7 @@ public class DomainHistory extends HistoryEntry {
|
||||
.map(GracePeriod::createFromHistory)
|
||||
.collect(toImmutableSet());
|
||||
domainBase.dsData =
|
||||
dsDataHistories.stream().map(DelegationSignerData::create).collect(toImmutableSet());
|
||||
dsDataHistories.stream().map(DomainDsData::create).collect(toImmutableSet());
|
||||
// Normally Hibernate would see that the domain fields are all null and would fill
|
||||
// domainBase with a null object. Unfortunately, the updateTimestamp is never null in SQL.
|
||||
if (domainBase.getDomainName() == null) {
|
||||
|
||||
@@ -30,28 +30,30 @@ import org.joda.time.DateTime;
|
||||
|
||||
/** The {@link ResponseData} returned for an EPP info flow on a domain. */
|
||||
@XmlRootElement(name = "infData")
|
||||
@XmlType(propOrder = {
|
||||
"fullyQualifiedDomainName",
|
||||
"repoId",
|
||||
"statusValues",
|
||||
"registrant",
|
||||
"contacts",
|
||||
"nameservers",
|
||||
"subordinateHosts",
|
||||
"currentSponsorClientId",
|
||||
"creationClientId",
|
||||
"creationTime",
|
||||
"lastEppUpdateClientId",
|
||||
"lastEppUpdateTime",
|
||||
"registrationExpirationTime",
|
||||
"lastTransferTime",
|
||||
"authInfo"})
|
||||
@XmlType(
|
||||
propOrder = {
|
||||
"domainName",
|
||||
"repoId",
|
||||
"statusValues",
|
||||
"registrant",
|
||||
"contacts",
|
||||
"nameservers",
|
||||
"subordinateHosts",
|
||||
"currentSponsorClientId",
|
||||
"creationClientId",
|
||||
"creationTime",
|
||||
"lastEppUpdateClientId",
|
||||
"lastEppUpdateTime",
|
||||
"registrationExpirationTime",
|
||||
"lastTransferTime",
|
||||
"authInfo"
|
||||
})
|
||||
@AutoValue
|
||||
@CopyAnnotations
|
||||
public abstract class DomainInfoData implements ResponseData {
|
||||
|
||||
@XmlElement(name = "name")
|
||||
abstract String getFullyQualifiedDomainName();
|
||||
abstract String getDomainName();
|
||||
|
||||
@XmlElement(name = "roid")
|
||||
abstract String getRepoId();
|
||||
@@ -110,7 +112,8 @@ public abstract class DomainInfoData implements ResponseData {
|
||||
/** Builder for {@link DomainInfoData}. */
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
public abstract Builder setFullyQualifiedDomainName(String fullyQualifiedDomainName);
|
||||
public abstract Builder setDomainName(String domainName);
|
||||
|
||||
public abstract Builder setRepoId(String repoId);
|
||||
public abstract Builder setStatusValues(@Nullable ImmutableSet<StatusValue> statusValues);
|
||||
public abstract Builder setRegistrant(String registrant);
|
||||
|
||||
+14
-17
@@ -17,7 +17,7 @@ package google.registry.model.domain.secdns;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData.DomainDsDataId;
|
||||
import google.registry.model.domain.secdns.DomainDsData.DomainDsDataId;
|
||||
import java.io.Serializable;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
@@ -34,15 +34,14 @@ import javax.xml.bind.annotation.XmlType;
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5910">RFC 5910</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4034">RFC 4034</a>
|
||||
* <p>TODO(b/177567432): Rename this class to DomainDsData.
|
||||
*/
|
||||
@XmlType(name = "dsData")
|
||||
@Entity
|
||||
@IdClass(DomainDsDataId.class)
|
||||
@Table(indexes = @Index(columnList = "domainRepoId"))
|
||||
public class DelegationSignerData extends DomainDsDataBase {
|
||||
@Table(name = "DelegationSignerData", indexes = @Index(columnList = "domainRepoId"))
|
||||
public class DomainDsData extends DomainDsDataBase {
|
||||
|
||||
private DelegationSignerData() {}
|
||||
private DomainDsData() {}
|
||||
|
||||
@Override
|
||||
@Id
|
||||
@@ -79,21 +78,21 @@ public class DelegationSignerData extends DomainDsDataBase {
|
||||
return super.getDigest();
|
||||
}
|
||||
|
||||
public DelegationSignerData cloneWithDomainRepoId(String domainRepoId) {
|
||||
DelegationSignerData clone = clone(this);
|
||||
public DomainDsData cloneWithDomainRepoId(String domainRepoId) {
|
||||
DomainDsData clone = clone(this);
|
||||
clone.domainRepoId = checkArgumentNotNull(domainRepoId);
|
||||
return clone;
|
||||
}
|
||||
|
||||
public DelegationSignerData cloneWithoutDomainRepoId() {
|
||||
DelegationSignerData clone = clone(this);
|
||||
public DomainDsData cloneWithoutDomainRepoId() {
|
||||
DomainDsData clone = clone(this);
|
||||
clone.domainRepoId = null;
|
||||
return clone;
|
||||
}
|
||||
|
||||
public static DelegationSignerData create(
|
||||
public static DomainDsData create(
|
||||
int keyTag, int algorithm, int digestType, byte[] digest, String domainRepoId) {
|
||||
DelegationSignerData instance = new DelegationSignerData();
|
||||
DomainDsData instance = new DomainDsData();
|
||||
instance.keyTag = keyTag;
|
||||
instance.algorithm = algorithm;
|
||||
instance.digestType = digestType;
|
||||
@@ -102,17 +101,15 @@ public class DelegationSignerData extends DomainDsDataBase {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static DelegationSignerData create(
|
||||
int keyTag, int algorithm, int digestType, byte[] digest) {
|
||||
public static DomainDsData create(int keyTag, int algorithm, int digestType, byte[] digest) {
|
||||
return create(keyTag, algorithm, digestType, digest, null);
|
||||
}
|
||||
|
||||
public static DelegationSignerData create(
|
||||
int keyTag, int algorithm, int digestType, String digestAsHex) {
|
||||
public static DomainDsData create(int keyTag, int algorithm, int digestType, String digestAsHex) {
|
||||
return create(keyTag, algorithm, digestType, DatatypeConverter.parseHexBinary(digestAsHex));
|
||||
}
|
||||
|
||||
public static DelegationSignerData create(DomainDsDataHistory history) {
|
||||
public static DomainDsData create(DomainDsDataHistory history) {
|
||||
return create(
|
||||
history.keyTag,
|
||||
history.algorithm,
|
||||
@@ -121,7 +118,7 @@ public class DelegationSignerData extends DomainDsDataBase {
|
||||
history.domainRepoId);
|
||||
}
|
||||
|
||||
/** Class to represent the composite primary key of {@link DelegationSignerData} entity. */
|
||||
/** Class to represent the composite primary key of {@link DomainDsData} entity. */
|
||||
static class DomainDsDataId extends ImmutableObject implements Serializable {
|
||||
|
||||
String domainRepoId;
|
||||
@@ -26,7 +26,7 @@ import javax.xml.bind.annotation.XmlTransient;
|
||||
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
|
||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
|
||||
/** Base class for {@link DelegationSignerData} and {@link DomainDsDataHistory}. */
|
||||
/** Base class for {@link DomainDsData} and {@link DomainDsDataHistory}. */
|
||||
@MappedSuperclass
|
||||
@Access(AccessType.FIELD)
|
||||
public abstract class DomainDsDataBase extends ImmutableObject implements UnsafeSerializable {
|
||||
|
||||
@@ -25,7 +25,7 @@ import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/** Entity class to represent a historic {@link DelegationSignerData}. */
|
||||
/** Entity class to represent a historic {@link DomainDsData}. */
|
||||
@Entity
|
||||
public class DomainDsDataHistory extends DomainDsDataBase implements UnsafeSerializable {
|
||||
|
||||
@@ -39,10 +39,9 @@ public class DomainDsDataHistory extends DomainDsDataBase implements UnsafeSeria
|
||||
|
||||
/**
|
||||
* Creates a {@link DomainDsDataHistory} instance from given {@link #domainHistoryRevisionId} and
|
||||
* {@link DelegationSignerData} instance.
|
||||
* {@link DomainDsData} instance.
|
||||
*/
|
||||
public static DomainDsDataHistory createFrom(
|
||||
long domainHistoryRevisionId, DelegationSignerData dsData) {
|
||||
public static DomainDsDataHistory createFrom(long domainHistoryRevisionId, DomainDsData dsData) {
|
||||
DomainDsDataHistory instance = new DomainDsDataHistory();
|
||||
instance.domainHistoryRevisionId = domainHistoryRevisionId;
|
||||
instance.domainRepoId = dsData.domainRepoId;
|
||||
|
||||
@@ -36,13 +36,13 @@ public class SecDnsCreateExtension extends ImmutableObject implements CommandExt
|
||||
Long maxSigLife;
|
||||
|
||||
/** Signatures for this domain. */
|
||||
Set<DelegationSignerData> dsData;
|
||||
Set<DomainDsData> dsData;
|
||||
|
||||
public Long getMaxSigLife() {
|
||||
return maxSigLife;
|
||||
}
|
||||
|
||||
public ImmutableSet<DelegationSignerData> getDsData() {
|
||||
public ImmutableSet<DomainDsData> getDsData() {
|
||||
return nullSafeImmutableCopy(dsData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ import javax.xml.bind.annotation.XmlRootElement;
|
||||
public class SecDnsInfoExtension extends ImmutableObject implements ResponseExtension {
|
||||
|
||||
/** Signatures for this domain. */
|
||||
ImmutableSet<DelegationSignerData> dsData;
|
||||
ImmutableSet<DomainDsData> dsData;
|
||||
|
||||
public static SecDnsInfoExtension create(ImmutableSet<DelegationSignerData> dsData) {
|
||||
public static SecDnsInfoExtension create(ImmutableSet<DomainDsData> dsData) {
|
||||
SecDnsInfoExtension instance = new SecDnsInfoExtension();
|
||||
instance.dsData = dsData;
|
||||
return instance;
|
||||
|
||||
@@ -70,9 +70,9 @@ public class SecDnsUpdateExtension extends ImmutableObject implements CommandExt
|
||||
@XmlTransient
|
||||
abstract static class AddRemoveBase extends ImmutableObject {
|
||||
/** Delegations to add or remove. */
|
||||
Set<DelegationSignerData> dsData;
|
||||
Set<DomainDsData> dsData;
|
||||
|
||||
public ImmutableSet<DelegationSignerData> getDsData() {
|
||||
public ImmutableSet<DomainDsData> getDsData() {
|
||||
return nullToEmptyImmutableCopy(dsData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.model.domain.token;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
@@ -28,6 +29,8 @@ import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import org.hibernate.annotations.Columns;
|
||||
import org.hibernate.annotations.Type;
|
||||
@@ -40,7 +43,9 @@ import org.joda.time.DateTime;
|
||||
public class PackagePromotion extends ImmutableObject implements Buildable {
|
||||
|
||||
/** An autogenerated identifier for the package promotion. */
|
||||
@Id long packagePromotionId;
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
long packagePromotionId;
|
||||
|
||||
/** The allocation token string for the package. */
|
||||
@Column(nullable = false)
|
||||
@@ -94,6 +99,21 @@ public class PackagePromotion extends ImmutableObject implements Buildable {
|
||||
return Optional.ofNullable(lastNotificationSent);
|
||||
}
|
||||
|
||||
/** Loads and returns a PackagePromotion entity by its token string directly from Cloud SQL. */
|
||||
public static Optional<PackagePromotion> loadByTokenString(String tokenString) {
|
||||
jpaTm().assertInTransaction();
|
||||
return jpaTm()
|
||||
.query("FROM PackagePromotion WHERE token = :token", PackagePromotion.class)
|
||||
.setParameter("token", VKey.createSql(AllocationToken.class, tokenString))
|
||||
.getResultStream()
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VKey<PackagePromotion> createVKey() {
|
||||
return VKey.createSql(PackagePromotion.class, packagePromotionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
@@ -143,7 +163,7 @@ public class PackagePromotion extends ImmutableObject implements Buildable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setNextBillingDate(@Nullable DateTime nextBillingDate) {
|
||||
public Builder setNextBillingDate(DateTime nextBillingDate) {
|
||||
checkArgumentNotNull(nextBillingDate, "Next billing date must not be null");
|
||||
getInstance().nextBillingDate = nextBillingDate;
|
||||
return this;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package google.registry.model.host;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import google.registry.model.EppResource.ForeignKeyedEppResource;
|
||||
import google.registry.model.annotations.ExternalMessagingName;
|
||||
@@ -52,7 +51,7 @@ import javax.persistence.AccessType;
|
||||
@javax.persistence.Index(columnList = "currentSponsorRegistrarId")
|
||||
})
|
||||
@ExternalMessagingName("host")
|
||||
@WithStringVKey
|
||||
@WithStringVKey(compositeKey = true)
|
||||
@Access(AccessType.FIELD) // otherwise it'll use the default if the repoId (property)
|
||||
public class Host extends HostBase implements ForeignKeyedEppResource {
|
||||
|
||||
@@ -65,7 +64,7 @@ public class Host extends HostBase implements ForeignKeyedEppResource {
|
||||
|
||||
@Override
|
||||
public VKey<Host> createVKey() {
|
||||
return VKey.create(Host.class, getRepoId(), Key.create(this));
|
||||
return VKey.createSql(Host.class, getRepoId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,7 +35,6 @@ import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -64,10 +63,7 @@ public class HostBase extends EppResource {
|
||||
* from (creationTime, deletionTime) there can only be one host in Datastore with this name.
|
||||
* However, there can be many hosts with the same name and non-overlapping lifetimes.
|
||||
*/
|
||||
// TODO(b/177567432): Rename this to hostName when we are off Datastore
|
||||
@Index
|
||||
@Column(name = "hostName")
|
||||
String fullyQualifiedHostName;
|
||||
@Index String hostName;
|
||||
|
||||
/** IP Addresses for this host. Can be null if this is an external host. */
|
||||
@Index Set<InetAddress> inetAddresses;
|
||||
@@ -95,7 +91,7 @@ public class HostBase extends EppResource {
|
||||
DateTime lastSuperordinateChange;
|
||||
|
||||
public String getHostName() {
|
||||
return fullyQualifiedHostName;
|
||||
return hostName;
|
||||
}
|
||||
|
||||
public VKey<Domain> getSuperordinateDomain() {
|
||||
@@ -120,7 +116,7 @@ public class HostBase extends EppResource {
|
||||
|
||||
@Override
|
||||
public String getForeignKey() {
|
||||
return fullyQualifiedHostName;
|
||||
return hostName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -197,7 +193,7 @@ public class HostBase extends EppResource {
|
||||
hostName.equals(canonicalizeHostname(hostName)),
|
||||
"Host name %s not in puny-coded, lower-case form",
|
||||
hostName);
|
||||
getInstance().fullyQualifiedHostName = hostName;
|
||||
getInstance().hostName = hostName;
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ public class HostCommand {
|
||||
@XmlTransient
|
||||
abstract static class HostCreateOrChange extends AbstractSingleResourceCommand
|
||||
implements ResourceCreateOrChange<Host.Builder> {
|
||||
public String getFullyQualifiedHostName() {
|
||||
public String getHostName() {
|
||||
return getTargetId();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,23 +28,25 @@ import org.joda.time.DateTime;
|
||||
|
||||
/** The {@link ResponseData} returned for an EPP info flow on a host. */
|
||||
@XmlRootElement(name = "infData")
|
||||
@XmlType(propOrder = {
|
||||
"fullyQualifiedHostName",
|
||||
"repoId",
|
||||
"statusValues",
|
||||
"inetAddresses",
|
||||
"currentSponsorClientId",
|
||||
"creationClientId",
|
||||
"creationTime",
|
||||
"lastEppUpdateClientId",
|
||||
"lastEppUpdateTime",
|
||||
"lastTransferTime" })
|
||||
@XmlType(
|
||||
propOrder = {
|
||||
"hostName",
|
||||
"repoId",
|
||||
"statusValues",
|
||||
"inetAddresses",
|
||||
"currentSponsorClientId",
|
||||
"creationClientId",
|
||||
"creationTime",
|
||||
"lastEppUpdateClientId",
|
||||
"lastEppUpdateTime",
|
||||
"lastTransferTime"
|
||||
})
|
||||
@AutoValue
|
||||
@CopyAnnotations
|
||||
public abstract class HostInfoData implements ResponseData {
|
||||
|
||||
@XmlElement(name = "name")
|
||||
abstract String getFullyQualifiedHostName();
|
||||
abstract String getHostName();
|
||||
|
||||
@XmlElement(name = "roid")
|
||||
abstract String getRepoId();
|
||||
@@ -79,7 +81,8 @@ public abstract class HostInfoData implements ResponseData {
|
||||
/** Builder for {@link HostInfoData}. */
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
public abstract Builder setFullyQualifiedHostName(String fullyQualifiedHostName);
|
||||
public abstract Builder setHostName(String hostName);
|
||||
|
||||
public abstract Builder setRepoId(String repoId);
|
||||
public abstract Builder setStatusValues(ImmutableSet<StatusValue> statusValues);
|
||||
public abstract Builder setInetAddresses(ImmutableSet<InetAddress> inetAddresses);
|
||||
|
||||
@@ -1,311 +0,0 @@
|
||||
// Copyright 2017 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.index;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.config.RegistryConfig.getEppResourceCachingDuration;
|
||||
import static google.registry.config.RegistryConfig.getEppResourceMaxCachedEntries;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaJpaTm;
|
||||
import static google.registry.util.CollectionUtils.entriesToImmutableMap;
|
||||
import static google.registry.util.TypeUtils.instantiate;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import com.google.common.collect.Streams;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.model.BackupGroupRoot;
|
||||
import google.registry.model.CacheUtils;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Class to map a foreign key to the active instance of {@link EppResource} whose unique id matches
|
||||
* the foreign key string. The instance is never deleted, but it is updated if a newer entity
|
||||
* becomes the active entity.
|
||||
*/
|
||||
public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroupRoot {
|
||||
|
||||
/** The {@link ForeignKeyIndex} type for {@link Contact} entities. */
|
||||
public static class ForeignKeyContactIndex extends ForeignKeyIndex<Contact> {}
|
||||
|
||||
/** The {@link ForeignKeyIndex} type for {@link Domain} entities. */
|
||||
public static class ForeignKeyDomainIndex extends ForeignKeyIndex<Domain> {}
|
||||
|
||||
/** The {@link ForeignKeyIndex} type for {@link Host} entities. */
|
||||
public static class ForeignKeyHostIndex extends ForeignKeyIndex<Host> {}
|
||||
|
||||
private static final ImmutableBiMap<
|
||||
Class<? extends EppResource>, Class<? extends ForeignKeyIndex<?>>>
|
||||
RESOURCE_CLASS_TO_FKI_CLASS =
|
||||
ImmutableBiMap.of(
|
||||
Contact.class, ForeignKeyContactIndex.class,
|
||||
Domain.class, ForeignKeyDomainIndex.class,
|
||||
Host.class, ForeignKeyHostIndex.class);
|
||||
|
||||
private static final ImmutableMap<Class<? extends EppResource>, String>
|
||||
RESOURCE_CLASS_TO_FKI_PROPERTY =
|
||||
ImmutableMap.of(
|
||||
Contact.class, "contactId",
|
||||
Domain.class, "fullyQualifiedDomainName",
|
||||
Host.class, "fullyQualifiedHostName");
|
||||
|
||||
String foreignKey;
|
||||
|
||||
/**
|
||||
* The deletion time of this {@link ForeignKeyIndex}.
|
||||
*
|
||||
* <p>This will generally be equal to the deletion time of {@link #reference}. However, in the
|
||||
* case of a {@link Host} that was renamed, this field will hold the time of the rename.
|
||||
*/
|
||||
DateTime deletionTime;
|
||||
|
||||
/** The referenced resource. */
|
||||
VKey<E> reference;
|
||||
|
||||
public String getForeignKey() {
|
||||
return foreignKey;
|
||||
}
|
||||
|
||||
public DateTime getDeletionTime() {
|
||||
return deletionTime;
|
||||
}
|
||||
|
||||
public VKey<E> getResourceKey() {
|
||||
return reference;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends EppResource> Class<ForeignKeyIndex<T>> mapToFkiClass(
|
||||
Class<T> resourceClass) {
|
||||
return (Class<ForeignKeyIndex<T>>) RESOURCE_CLASS_TO_FKI_CLASS.get(resourceClass);
|
||||
}
|
||||
|
||||
/** Create a {@link ForeignKeyIndex} instance for a resource, expiring at a specified time. */
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <E extends EppResource> ForeignKeyIndex<E> create(
|
||||
E resource, DateTime deletionTime) {
|
||||
Class<E> resourceClass = (Class<E>) resource.getClass();
|
||||
ForeignKeyIndex<E> instance = instantiate(mapToFkiClass(resourceClass));
|
||||
instance.reference = (VKey<E>) resource.createVKey();
|
||||
instance.foreignKey = resource.getForeignKey();
|
||||
instance.deletionTime = deletionTime;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a {@link VKey} to an {@link EppResource} from the database by foreign key.
|
||||
*
|
||||
* <p>Returns null if no foreign key index with this foreign key was ever created, or if the most
|
||||
* recently created foreign key index was deleted before time "now". This method does not actually
|
||||
* check that the referenced resource actually exists. However, for normal epp resources, it is
|
||||
* safe to assume that the referenced resource exists if the foreign key index does.
|
||||
*
|
||||
* @param clazz the resource type to load
|
||||
* @param foreignKey id to match
|
||||
* @param now the current logical time to use when checking for soft deletion of the foreign key
|
||||
* index
|
||||
*/
|
||||
@Nullable
|
||||
public static <E extends EppResource> VKey<E> loadAndGetKey(
|
||||
Class<E> clazz, String foreignKey, DateTime now) {
|
||||
ForeignKeyIndex<E> index = load(clazz, foreignKey, now);
|
||||
return index == null ? null : index.getResourceKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a {@link ForeignKeyIndex} by class and id string that is active at or after the specified
|
||||
* moment in time.
|
||||
*
|
||||
* <p>This will return null if the {@link ForeignKeyIndex} doesn't exist or has been soft deleted.
|
||||
*/
|
||||
@Nullable
|
||||
public static <E extends EppResource> ForeignKeyIndex<E> load(
|
||||
Class<E> clazz, String foreignKey, DateTime now) {
|
||||
return load(clazz, ImmutableList.of(foreignKey), now).get(foreignKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a map of {@link ForeignKeyIndex} instances by class and id strings that are active at or
|
||||
* after the specified moment in time.
|
||||
*
|
||||
* <p>The returned map will omit any keys for which the {@link ForeignKeyIndex} doesn't exist or
|
||||
* has been soft deleted.
|
||||
*/
|
||||
public static <E extends EppResource> ImmutableMap<String, ForeignKeyIndex<E>> load(
|
||||
Class<E> clazz, Collection<String> foreignKeys, final DateTime now) {
|
||||
return loadIndexesFromStore(clazz, foreignKeys, false).entrySet().stream()
|
||||
.filter(e -> now.isBefore(e.getValue().getDeletionTime()))
|
||||
.collect(entriesToImmutableMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to load all of the most recent {@link ForeignKeyIndex}es for the given foreign
|
||||
* keys, regardless of whether or not they have been soft-deleted.
|
||||
*
|
||||
* <p>Used by both the cached (w/o deletion check) and the non-cached (with deletion check) calls.
|
||||
*/
|
||||
private static <E extends EppResource>
|
||||
ImmutableMap<String, ForeignKeyIndex<E>> loadIndexesFromStore(
|
||||
Class<E> clazz, Collection<String> foreignKeys, boolean useReplicaJpaTm) {
|
||||
String property = RESOURCE_CLASS_TO_FKI_PROPERTY.get(clazz);
|
||||
JpaTransactionManager jpaTmToUse = useReplicaJpaTm ? replicaJpaTm() : jpaTm();
|
||||
ImmutableList<ForeignKeyIndex<E>> indexes =
|
||||
jpaTmToUse.transact(
|
||||
() ->
|
||||
jpaTmToUse
|
||||
.criteriaQuery(
|
||||
CriteriaQueryBuilder.create(clazz)
|
||||
.whereFieldIsIn(property, foreignKeys)
|
||||
.build())
|
||||
.getResultStream()
|
||||
.map(e -> create(e, e.getDeletionTime()))
|
||||
.collect(toImmutableList()));
|
||||
// We need to find and return the entities with the maximum deletionTime for each foreign key.
|
||||
return Multimaps.index(indexes, ForeignKeyIndex::getForeignKey).asMap().entrySet().stream()
|
||||
.map(
|
||||
entry ->
|
||||
Maps.immutableEntry(
|
||||
entry.getKey(),
|
||||
entry.getValue().stream()
|
||||
.max(Comparator.comparing(ForeignKeyIndex::getDeletionTime))
|
||||
.get()))
|
||||
.collect(entriesToImmutableMap());
|
||||
}
|
||||
|
||||
static final CacheLoader<VKey<ForeignKeyIndex<?>>, Optional<ForeignKeyIndex<?>>> CACHE_LOADER =
|
||||
new CacheLoader<VKey<ForeignKeyIndex<?>>, Optional<ForeignKeyIndex<?>>>() {
|
||||
|
||||
@Override
|
||||
public Optional<ForeignKeyIndex<?>> load(VKey<ForeignKeyIndex<?>> key) {
|
||||
String foreignKey = key.getSqlKey().toString();
|
||||
return Optional.ofNullable(
|
||||
loadIndexesFromStore(
|
||||
RESOURCE_CLASS_TO_FKI_CLASS.inverse().get(key.getKind()),
|
||||
ImmutableSet.of(foreignKey),
|
||||
true)
|
||||
.get(foreignKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<VKey<ForeignKeyIndex<?>>, Optional<ForeignKeyIndex<?>>> loadAll(
|
||||
Iterable<? extends VKey<ForeignKeyIndex<?>>> keys) {
|
||||
if (!keys.iterator().hasNext()) {
|
||||
return ImmutableMap.of();
|
||||
}
|
||||
Class<? extends EppResource> resourceClass =
|
||||
RESOURCE_CLASS_TO_FKI_CLASS.inverse().get(keys.iterator().next().getKind());
|
||||
ImmutableSet<String> foreignKeys =
|
||||
Streams.stream(keys).map(v -> v.getSqlKey().toString()).collect(toImmutableSet());
|
||||
ImmutableSet<VKey<ForeignKeyIndex<?>>> typedKeys = ImmutableSet.copyOf(keys);
|
||||
ImmutableMap<String, ? extends ForeignKeyIndex<? extends EppResource>> existingFkis =
|
||||
loadIndexesFromStore(resourceClass, foreignKeys, true);
|
||||
// ofy omits keys that don't have values in Datastore, so re-add them in
|
||||
// here with Optional.empty() values.
|
||||
return Maps.asMap(
|
||||
typedKeys,
|
||||
(VKey<ForeignKeyIndex<?>> key) ->
|
||||
Optional.ofNullable(existingFkis.getOrDefault(key.getSqlKey().toString(), null)));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A limited size, limited time cache for foreign key entities.
|
||||
*
|
||||
* <p>This is only used to cache foreign key entities for the purposes of checking whether they
|
||||
* exist (and if so, what entity they point to) during a few domain flows. Any other operations on
|
||||
* foreign keys should not use this cache.
|
||||
*
|
||||
* <p>Note that the value type of this cache is Optional because the foreign keys in question are
|
||||
* coming from external commands, and thus don't necessarily represent entities in our system that
|
||||
* actually exist. So we cache the fact that they *don't* exist by using Optional.empty(), and
|
||||
* then several layers up the EPP command will fail with an error message like "The contact with
|
||||
* given IDs (blah) don't exist."
|
||||
*/
|
||||
@NonFinalForTesting
|
||||
private static LoadingCache<VKey<ForeignKeyIndex<?>>, Optional<ForeignKeyIndex<?>>>
|
||||
cacheForeignKeyIndexes = createForeignKeyIndexesCache(getEppResourceCachingDuration());
|
||||
|
||||
private static LoadingCache<VKey<ForeignKeyIndex<?>>, Optional<ForeignKeyIndex<?>>>
|
||||
createForeignKeyIndexesCache(Duration expiry) {
|
||||
return CacheUtils.newCacheBuilder(expiry)
|
||||
.maximumSize(getEppResourceMaxCachedEntries())
|
||||
.build(CACHE_LOADER);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setCacheForTest(Optional<Duration> expiry) {
|
||||
Duration effectiveExpiry = expiry.orElse(getEppResourceCachingDuration());
|
||||
cacheForeignKeyIndexes = createForeignKeyIndexesCache(effectiveExpiry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a list of {@link ForeignKeyIndex} instances by class and id strings that are active at or
|
||||
* after the specified moment in time, using the cache if enabled.
|
||||
*
|
||||
* <p>The returned map will omit any keys for which the {@link ForeignKeyIndex} doesn't exist or
|
||||
* has been soft deleted.
|
||||
*
|
||||
* <p>Don't use the cached version of this method unless you really need it for performance
|
||||
* reasons, and are OK with the trade-offs in loss of transactional consistency.
|
||||
*/
|
||||
public static <E extends EppResource> ImmutableMap<String, ForeignKeyIndex<E>> loadCached(
|
||||
Class<E> clazz, Collection<String> foreignKeys, final DateTime now) {
|
||||
if (!RegistryConfig.isEppResourceCachingEnabled()) {
|
||||
return load(clazz, foreignKeys, now);
|
||||
}
|
||||
Class<? extends ForeignKeyIndex<?>> fkiClass = mapToFkiClass(clazz);
|
||||
// Safe to cast VKey<FKI<E>> to VKey<FKI<?>>
|
||||
@SuppressWarnings("unchecked")
|
||||
ImmutableList<VKey<ForeignKeyIndex<?>>> fkiVKeys =
|
||||
foreignKeys.stream()
|
||||
.map(fk -> (VKey<ForeignKeyIndex<?>>) VKey.createSql(fkiClass, fk))
|
||||
.collect(toImmutableList());
|
||||
// This cast is safe because when we loaded ForeignKeyIndexes above we used type clazz, which
|
||||
// is scoped to E.
|
||||
@SuppressWarnings("unchecked")
|
||||
ImmutableMap<String, ForeignKeyIndex<E>> fkisFromCache =
|
||||
cacheForeignKeyIndexes.getAll(fkiVKeys).entrySet().stream()
|
||||
.filter(entry -> entry.getValue().isPresent())
|
||||
.filter(entry -> now.isBefore(entry.getValue().get().getDeletionTime()))
|
||||
.collect(
|
||||
toImmutableMap(
|
||||
entry -> entry.getKey().getSqlKey().toString(),
|
||||
entry -> (ForeignKeyIndex<E>) entry.getValue().get()));
|
||||
return fkisFromCache;
|
||||
}
|
||||
}
|
||||
+4
-8
@@ -91,10 +91,10 @@ public class PendingActionNotificationResponse extends ImmutableObject
|
||||
}
|
||||
|
||||
public static DomainPendingActionNotificationResponse create(
|
||||
String fullyQualifiedDomainName, boolean actionResult, Trid trid, DateTime processedDate) {
|
||||
String domainName, boolean actionResult, Trid trid, DateTime processedDate) {
|
||||
return init(
|
||||
new DomainPendingActionNotificationResponse(),
|
||||
fullyQualifiedDomainName,
|
||||
domainName,
|
||||
actionResult,
|
||||
trid,
|
||||
processedDate);
|
||||
@@ -140,13 +140,9 @@ public class PendingActionNotificationResponse extends ImmutableObject
|
||||
}
|
||||
|
||||
public static HostPendingActionNotificationResponse create(
|
||||
String fullyQualifiedHostName, boolean actionResult, Trid trid, DateTime processedDate) {
|
||||
String hostName, boolean actionResult, Trid trid, DateTime processedDate) {
|
||||
return init(
|
||||
new HostPendingActionNotificationResponse(),
|
||||
fullyQualifiedHostName,
|
||||
actionResult,
|
||||
trid,
|
||||
processedDate);
|
||||
new HostPendingActionNotificationResponse(), hostName, actionResult, trid, processedDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +386,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||
TransferResponse transferResponse;
|
||||
|
||||
@Column(name = "transfer_response_domain_name")
|
||||
String fullyQualifiedDomainName;
|
||||
String domainName;
|
||||
|
||||
@Column(name = "transfer_response_domain_expiration_time")
|
||||
DateTime extendedRegistrationExpirationTime;
|
||||
@@ -427,7 +427,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||
pendingActionNotificationResponse.getActionResult(),
|
||||
pendingActionNotificationResponse.getTrid(),
|
||||
pendingActionNotificationResponse.processedDate);
|
||||
} else if (fullyQualifiedDomainName != null) {
|
||||
} else if (domainName != null) {
|
||||
pendingActionNotificationResponse =
|
||||
DomainPendingActionNotificationResponse.create(
|
||||
pendingActionNotificationResponse.nameOrId.value,
|
||||
@@ -457,10 +457,10 @@ public abstract class PollMessage extends ImmutableObject
|
||||
.setPendingTransferExpirationTime(
|
||||
transferResponse.getPendingTransferExpirationTime())
|
||||
.build();
|
||||
} else if (fullyQualifiedDomainName != null) {
|
||||
} else if (domainName != null) {
|
||||
transferResponse =
|
||||
new DomainTransferResponse.Builder()
|
||||
.setFullyQualifiedDomainName(fullyQualifiedDomainName)
|
||||
.setDomainName(domainName)
|
||||
.setGainingRegistrarId(transferResponse.getGainingRegistrarId())
|
||||
.setLosingRegistrarId(transferResponse.getLosingRegistrarId())
|
||||
.setTransferStatus(transferResponse.getTransferStatus())
|
||||
@@ -486,7 +486,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||
OneTime instance = getInstance();
|
||||
// Note: In its current form, the code will basically just ignore everything but the first
|
||||
// PendingActionNotificationResponse and TransferResponse in responseData, and will override
|
||||
// any identifier fields (e.g. contactId, fullyQualifiedDomainName) obtained from the
|
||||
// any identifier fields (e.g. contactId, domainName) obtained from the
|
||||
// PendingActionNotificationResponse if a TransferResponse is found with different values
|
||||
// for those fields. It is not clear what the constraints should be on this data or
|
||||
// whether we should enforce them here, though historically we have not, so the current
|
||||
@@ -507,8 +507,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||
instance.contactId = instance.pendingActionNotificationResponse.nameOrId.value;
|
||||
} else if (instance.pendingActionNotificationResponse
|
||||
instanceof DomainPendingActionNotificationResponse) {
|
||||
instance.fullyQualifiedDomainName =
|
||||
instance.pendingActionNotificationResponse.nameOrId.value;
|
||||
instance.domainName = instance.pendingActionNotificationResponse.nameOrId.value;
|
||||
} else if (instance.pendingActionNotificationResponse
|
||||
instanceof HostPendingActionNotificationResponse) {
|
||||
instance.hostId = instance.pendingActionNotificationResponse.nameOrId.value;
|
||||
@@ -527,7 +526,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||
instance.contactId = ((ContactTransferResponse) instance.transferResponse).getContactId();
|
||||
} else if (instance.transferResponse instanceof DomainTransferResponse) {
|
||||
DomainTransferResponse response = (DomainTransferResponse) instance.transferResponse;
|
||||
instance.fullyQualifiedDomainName = response.getFullyQualifiedDomainName();
|
||||
instance.domainName = response.getDomainName();
|
||||
instance.extendedRegistrationExpirationTime =
|
||||
response.getExtendedRegistrationExpirationTime();
|
||||
}
|
||||
|
||||
@@ -28,10 +28,10 @@ public interface PremiumPricingEngine {
|
||||
/**
|
||||
* Returns the prices for the given fully qualified domain name at the given time.
|
||||
*
|
||||
* <p>Note that the fullyQualifiedDomainName must only contain a single part left of the TLD, i.e.
|
||||
* subdomains are not allowed, but multi-part TLDs are.
|
||||
* <p>Note that the domainName must only contain a single part left of the TLD, i.e. subdomains
|
||||
* are not allowed, but multi-part TLDs are.
|
||||
*/
|
||||
DomainPrices getDomainPrices(String fullyQualifiedDomainName, DateTime priceTime);
|
||||
DomainPrices getDomainPrices(String domainName, DateTime priceTime);
|
||||
|
||||
/**
|
||||
* A class containing information on premium prices for a specific domain name.
|
||||
|
||||
+3
-3
@@ -34,9 +34,9 @@ public final class StaticPremiumListPricingEngine implements PremiumPricingEngin
|
||||
@Inject StaticPremiumListPricingEngine() {}
|
||||
|
||||
@Override
|
||||
public DomainPrices getDomainPrices(String fullyQualifiedDomainName, DateTime priceTime) {
|
||||
String tld = getTldFromDomainName(fullyQualifiedDomainName);
|
||||
String label = InternetDomainName.from(fullyQualifiedDomainName).parts().get(0);
|
||||
public DomainPrices getDomainPrices(String domainName, DateTime priceTime) {
|
||||
String tld = getTldFromDomainName(domainName);
|
||||
String label = InternetDomainName.from(domainName).parts().get(0);
|
||||
Registry registry = Registry.get(checkNotNull(tld, "tld"));
|
||||
Optional<Money> premiumPrice =
|
||||
registry.getPremiumListName().flatMap(pl -> PremiumListDao.getPremiumPrice(pl, label));
|
||||
|
||||
@@ -207,11 +207,10 @@ public class Registrar extends ImmutableObject
|
||||
* Unique registrar client id. Must conform to "clIDType" as defined in RFC5730.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5730#section-4.2">Shared Structure Schema</a>
|
||||
* <p>TODO(b/177567432): Rename this field to registrarId.
|
||||
*/
|
||||
@Id
|
||||
@Column(name = "registrarId", nullable = false)
|
||||
String clientIdentifier;
|
||||
@Column(nullable = false)
|
||||
String registrarId;
|
||||
|
||||
/**
|
||||
* Registrar name. This is a distinct from the client identifier since there are no restrictions
|
||||
@@ -270,9 +269,7 @@ public class Registrar extends ImmutableObject
|
||||
String failoverClientCertificateHash;
|
||||
|
||||
/** An allow list of netmasks (in CIDR notation) which the client is allowed to connect from. */
|
||||
// TODO(b/177567432): Rename to ipAddressAllowList once Cloud SQL migration is complete.
|
||||
@Column(name = "ip_address_allow_list")
|
||||
List<CidrAddressBlock> ipAddressWhitelist;
|
||||
List<CidrAddressBlock> ipAddressAllowList;
|
||||
|
||||
/** A hashed password for EPP access. The hash is a base64 encoded SHA256 string. */
|
||||
String passwordHash;
|
||||
@@ -408,7 +405,7 @@ public class Registrar extends ImmutableObject
|
||||
boolean registryLockAllowed = false;
|
||||
|
||||
public String getRegistrarId() {
|
||||
return clientIdentifier;
|
||||
return registrarId;
|
||||
}
|
||||
|
||||
public DateTime getCreationTime() {
|
||||
@@ -498,7 +495,7 @@ public class Registrar extends ImmutableObject
|
||||
}
|
||||
|
||||
public ImmutableList<CidrAddressBlock> getIpAddressAllowList() {
|
||||
return nullToEmptyImmutableCopy(ipAddressWhitelist);
|
||||
return nullToEmptyImmutableCopy(ipAddressAllowList);
|
||||
}
|
||||
|
||||
public RegistrarAddress getLocalizedAddress() {
|
||||
@@ -587,7 +584,7 @@ public class Registrar extends ImmutableObject
|
||||
() ->
|
||||
jpaTm()
|
||||
.query("FROM RegistrarPoc WHERE registrarId = :registrarId", RegistrarPoc.class)
|
||||
.setParameter("registrarId", clientIdentifier)
|
||||
.setParameter("registrarId", registrarId)
|
||||
.getResultStream()
|
||||
.collect(toImmutableList()));
|
||||
}
|
||||
@@ -595,7 +592,7 @@ public class Registrar extends ImmutableObject
|
||||
@Override
|
||||
public Map<String, Object> toJsonMap() {
|
||||
return new JsonMapBuilder()
|
||||
.put("clientIdentifier", clientIdentifier)
|
||||
.put("registrarId", registrarId)
|
||||
.put("ianaIdentifier", ianaIdentifier)
|
||||
.putString("creationTime", creationTime.getTimestamp())
|
||||
.putString("lastUpdateTime", lastUpdateTime.getTimestamp())
|
||||
@@ -655,7 +652,7 @@ public class Registrar extends ImmutableObject
|
||||
/** Creates a {@link VKey} for this instance. */
|
||||
@Override
|
||||
public VKey<Registrar> createVKey() {
|
||||
return createVKey(clientIdentifier);
|
||||
return createVKey(registrarId);
|
||||
}
|
||||
|
||||
/** Creates a {@link VKey} for the given {@code registrarId}. */
|
||||
@@ -678,7 +675,7 @@ public class Registrar extends ImmutableObject
|
||||
checkArgument(
|
||||
Range.closed(3, 16).contains(registrarId.length()),
|
||||
"Registrar ID must be 3-16 characters long.");
|
||||
getInstance().clientIdentifier = registrarId;
|
||||
getInstance().registrarId = registrarId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -796,7 +793,7 @@ public class Registrar extends ImmutableObject
|
||||
}
|
||||
|
||||
public Builder setIpAddressAllowList(Iterable<CidrAddressBlock> ipAddressAllowList) {
|
||||
getInstance().ipAddressWhitelist = ImmutableList.copyOf(ipAddressAllowList);
|
||||
getInstance().ipAddressAllowList = ImmutableList.copyOf(ipAddressAllowList);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,11 +72,9 @@ public class ClaimsList extends ImmutableObject {
|
||||
* <p>Note that the value of this field is parsed from the claims list file(See this <a
|
||||
* href="https://tools.ietf.org/html/draft-lozano-tmch-func-spec-08#section-6.1">RFC</>), it is
|
||||
* the DNL List creation datetime from the rfc.
|
||||
*
|
||||
* <p>TODO(b/177567432): Rename this field to tmdbGenerationTime.
|
||||
*/
|
||||
@Column(name = "tmdb_generation_time", nullable = false)
|
||||
DateTime creationTime;
|
||||
@Column(nullable = false)
|
||||
DateTime tmdbGenerationTime;
|
||||
|
||||
/**
|
||||
* A map from labels to claims keys.
|
||||
@@ -143,7 +141,7 @@ public class ClaimsList extends ImmutableObject {
|
||||
* creation datetime</a>
|
||||
*/
|
||||
public DateTime getTmdbGenerationTime() {
|
||||
return creationTime;
|
||||
return tmdbGenerationTime;
|
||||
}
|
||||
|
||||
/** Returns the creation time of this claims list. */
|
||||
@@ -225,7 +223,7 @@ public class ClaimsList extends ImmutableObject {
|
||||
public static ClaimsList create(
|
||||
DateTime tmdbGenerationTime, ImmutableMap<String, String> labelsToKeys) {
|
||||
ClaimsList instance = new ClaimsList();
|
||||
instance.creationTime = checkNotNull(tmdbGenerationTime);
|
||||
instance.tmdbGenerationTime = checkNotNull(tmdbGenerationTime);
|
||||
instance.labelsToKeys = checkNotNull(labelsToKeys);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -33,22 +33,24 @@ public class TransferResponse extends BaseTransferObject implements ResponseData
|
||||
|
||||
/** An adapter to output the XML in response to a transfer command on a domain. */
|
||||
@XmlRootElement(name = "trnData", namespace = "urn:ietf:params:xml:ns:domain-1.0")
|
||||
@XmlType(propOrder = {
|
||||
"fullyQualifiedDomainName",
|
||||
"transferStatus",
|
||||
"gainingClientId",
|
||||
"transferRequestTime",
|
||||
"losingClientId",
|
||||
"pendingTransferExpirationTime",
|
||||
"extendedRegistrationExpirationTime"},
|
||||
namespace = "urn:ietf:params:xml:ns:domain-1.0")
|
||||
@XmlType(
|
||||
propOrder = {
|
||||
"domainName",
|
||||
"transferStatus",
|
||||
"gainingClientId",
|
||||
"transferRequestTime",
|
||||
"losingClientId",
|
||||
"pendingTransferExpirationTime",
|
||||
"extendedRegistrationExpirationTime"
|
||||
},
|
||||
namespace = "urn:ietf:params:xml:ns:domain-1.0")
|
||||
public static class DomainTransferResponse extends TransferResponse {
|
||||
|
||||
@XmlElement(name = "name")
|
||||
String fullyQualifiedDomainName;
|
||||
String domainName;
|
||||
|
||||
public String getFullyQualifiedDomainName() {
|
||||
return fullyQualifiedDomainName;
|
||||
public String getDomainName() {
|
||||
return domainName;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,8 +67,8 @@ public class TransferResponse extends BaseTransferObject implements ResponseData
|
||||
/** Builder for {@link DomainTransferResponse}. */
|
||||
public static class Builder
|
||||
extends BaseTransferObject.Builder<DomainTransferResponse, Builder> {
|
||||
public Builder setFullyQualifiedDomainName(String fullyQualifiedDomainName) {
|
||||
getInstance().fullyQualifiedDomainName = fullyQualifiedDomainName;
|
||||
public Builder setDomainName(String domainName) {
|
||||
getInstance().domainName = domainName;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ public interface JpaTransactionManager extends TransactionManager {
|
||||
*/
|
||||
<T> TypedQuery<T> query(String sqlString, Class<T> resultClass);
|
||||
|
||||
/** Creates a JPA SQU query for the given criteria query. */
|
||||
/** Creates a JPA SQL query for the given criteria query. */
|
||||
<T> TypedQuery<T> criteriaQuery(CriteriaQuery<T> criteriaQuery);
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,7 +16,6 @@ package google.registry.rdap;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyCached;
|
||||
import static google.registry.model.index.ForeignKeyIndex.loadAndGetKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaJpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
@@ -36,6 +35,7 @@ import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.InetAddresses;
|
||||
import com.google.common.primitives.Booleans;
|
||||
import com.googlecode.objectify.cmd.Query;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
@@ -199,13 +199,12 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
final RdapSearchPattern partialStringQuery) {
|
||||
// We can't query for undeleted domains as part of the query itself; that would require an
|
||||
// inequality query on deletion time, and we are already using inequality queries on
|
||||
// fullyQualifiedDomainName. So we instead pick an arbitrary limit of
|
||||
// RESULT_SET_SIZE_SCALING_FACTOR times the result set size limit, fetch up to that many, and
|
||||
// weed out all deleted domains. If there still isn't a full result set's worth of domains, we
|
||||
// give up and return just the ones we found. Don't use queryItems, because it checks that the
|
||||
// initial string is at least a certain length, which we don't need in this case. Query the
|
||||
// domains directly, rather than the foreign keys, because then we have an index on TLD if we
|
||||
// need it.
|
||||
// domainName. So we instead pick an arbitrary limit of RESULT_SET_SIZE_SCALING_FACTOR times the
|
||||
// result set size limit, fetch up to that many, and weed out all deleted domains. If there
|
||||
// still isn't a full result set's worth of domains, we give up and return just the ones we
|
||||
// found. Don't use queryItems, because it checks that the initial string is at least a certain
|
||||
// length, which we don't need in this case. Query the domains directly, rather than the foreign
|
||||
// keys, because then we have an index on TLD if we need it.
|
||||
int querySizeLimit = RESULT_SET_SIZE_SCALING_FACTOR * rdapResultSetMaxSize;
|
||||
RdapResultSet<Domain> resultSet;
|
||||
if (tm().isOfy()) {
|
||||
@@ -213,10 +212,10 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
auditedOfy()
|
||||
.load()
|
||||
.type(Domain.class)
|
||||
.filter("fullyQualifiedDomainName <", partialStringQuery.getNextInitialString())
|
||||
.filter("fullyQualifiedDomainName >=", partialStringQuery.getInitialString());
|
||||
.filter("domainName <", partialStringQuery.getNextInitialString())
|
||||
.filter("domainName >=", partialStringQuery.getInitialString());
|
||||
if (cursorString.isPresent()) {
|
||||
query = query.filter("fullyQualifiedDomainName >", cursorString.get());
|
||||
query = query.filter("domainName >", cursorString.get());
|
||||
}
|
||||
if (partialStringQuery.getSuffix() != null) {
|
||||
query = query.filter("tld", partialStringQuery.getSuffix());
|
||||
@@ -234,16 +233,14 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
CriteriaQueryBuilder<Domain> queryBuilder =
|
||||
CriteriaQueryBuilder.create(replicaJpaTm(), Domain.class)
|
||||
.where(
|
||||
"fullyQualifiedDomainName",
|
||||
"domainName",
|
||||
criteriaBuilder::like,
|
||||
String.format("%s%%", partialStringQuery.getInitialString()))
|
||||
.orderByAsc("fullyQualifiedDomainName");
|
||||
.orderByAsc("domainName");
|
||||
if (cursorString.isPresent()) {
|
||||
queryBuilder =
|
||||
queryBuilder.where(
|
||||
"fullyQualifiedDomainName",
|
||||
criteriaBuilder::greaterThan,
|
||||
cursorString.get());
|
||||
"domainName", criteriaBuilder::greaterThan, cursorString.get());
|
||||
}
|
||||
if (partialStringQuery.getSuffix() != null) {
|
||||
queryBuilder =
|
||||
@@ -258,18 +255,18 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
|
||||
/** Searches for domains by domain name with a TLD suffix. */
|
||||
private DomainSearchResponse searchByDomainNameByTld(String tld) {
|
||||
// Even though we are not searching on fullyQualifiedDomainName, we want the results to come
|
||||
// back ordered by name, so we are still in the same boat as
|
||||
// searchByDomainNameWithInitialString, unable to perform an inequality query on deletion time.
|
||||
// Don't use queryItems, because it doesn't handle pending deletes.
|
||||
// Even though we are not searching on domainName, we want the results to come back ordered by
|
||||
// name, so we are still in the same boat as searchByDomainNameWithInitialString, unable to
|
||||
// perform an inequality query on deletion time. Don't use queryItems, because it doesn't handle
|
||||
// pending deletes.
|
||||
int querySizeLimit = RESULT_SET_SIZE_SCALING_FACTOR * rdapResultSetMaxSize;
|
||||
RdapResultSet<Domain> resultSet;
|
||||
if (tm().isOfy()) {
|
||||
Query<Domain> query = auditedOfy().load().type(Domain.class).filter("tld", tld);
|
||||
if (cursorString.isPresent()) {
|
||||
query = query.filter("fullyQualifiedDomainName >", cursorString.get());
|
||||
query = query.filter("domainName >", cursorString.get());
|
||||
}
|
||||
query = query.order("fullyQualifiedDomainName").limit(querySizeLimit);
|
||||
query = query.order("domainName").limit(querySizeLimit);
|
||||
resultSet = getMatchingResources(query, true, querySizeLimit);
|
||||
} else {
|
||||
resultSet =
|
||||
@@ -281,10 +278,10 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
Domain.class,
|
||||
"tld",
|
||||
tld,
|
||||
Optional.of("fullyQualifiedDomainName"),
|
||||
Optional.of("domainName"),
|
||||
cursorString,
|
||||
DeletedItemHandling.INCLUDE)
|
||||
.orderByAsc("fullyQualifiedDomainName");
|
||||
.orderByAsc("domainName");
|
||||
return getMatchingResourcesSql(builder, true, querySizeLimit);
|
||||
});
|
||||
}
|
||||
@@ -331,9 +328,9 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
return getNameserverRefsByLdhNameWithSuffix(partialStringQuery);
|
||||
}
|
||||
// If there's no suffix, query the host resources. Query the resources themselves, rather than
|
||||
// the foreign key indexes, because then we have an index on fully qualified host name and
|
||||
// deletion time, so we can check the deletion status in the query itself. The initial string
|
||||
// must be present, to avoid querying every host in the system. This restriction is enforced by
|
||||
// the foreign keys, because then we have an index on fully qualified host name and deletion
|
||||
// time, so we can check the deletion status in the query itself. The initial string must be
|
||||
// present, to avoid querying every host in the system. This restriction is enforced by
|
||||
// {@link queryItems}.
|
||||
//
|
||||
// Only return the first maxNameserversInFirstStage nameservers. This could result in an
|
||||
@@ -344,7 +341,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
Query<Host> query =
|
||||
queryItems(
|
||||
Host.class,
|
||||
"fullyQualifiedHostName",
|
||||
"hostName",
|
||||
partialStringQuery,
|
||||
Optional.empty(),
|
||||
DeletedItemHandling.EXCLUDE,
|
||||
@@ -362,7 +359,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
CriteriaQueryBuilder<Host> builder =
|
||||
queryItemsSql(
|
||||
Host.class,
|
||||
"fullyQualifiedHostName",
|
||||
"hostName",
|
||||
partialStringQuery,
|
||||
Optional.empty(),
|
||||
DeletedItemHandling.EXCLUDE);
|
||||
@@ -400,7 +397,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
: ImmutableList.of(host.get().createVKey());
|
||||
} else {
|
||||
VKey<Host> hostKey =
|
||||
loadAndGetKey(
|
||||
ForeignKeyUtils.load(
|
||||
Host.class,
|
||||
partialStringQuery.getInitialString(),
|
||||
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
|
||||
@@ -442,7 +439,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
}
|
||||
} else {
|
||||
VKey<Host> hostKey =
|
||||
loadAndGetKey(
|
||||
ForeignKeyUtils.load(
|
||||
Host.class, fqhn, shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
|
||||
if (hostKey != null) {
|
||||
builder.add(hostKey);
|
||||
@@ -558,7 +555,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
// If we are not performing an inequality query, we can filter on the cursor in the query.
|
||||
// Otherwise, we will need to filter the results afterward.
|
||||
} else if (cursorString.isPresent()) {
|
||||
query = query.filter("fullyQualifiedDomainName >", cursorString.get());
|
||||
query = query.filter("domainName >", cursorString.get());
|
||||
}
|
||||
Stream<Domain> stream = Streams.stream(query).filter(this::isAuthorized);
|
||||
if (cursorString.isPresent()) {
|
||||
@@ -574,7 +571,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
CriteriaQueryBuilder<Domain> queryBuilder =
|
||||
CriteriaQueryBuilder.create(replicaJpaTm(), Domain.class)
|
||||
.whereFieldContains("nsHosts", hostKey)
|
||||
.orderByAsc("fullyQualifiedDomainName");
|
||||
.orderByAsc("domainName");
|
||||
CriteriaBuilder criteriaBuilder =
|
||||
replicaJpaTm().getEntityManager().getCriteriaBuilder();
|
||||
if (!shouldIncludeDeleted()) {
|
||||
@@ -585,9 +582,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
if (cursorString.isPresent()) {
|
||||
queryBuilder =
|
||||
queryBuilder.where(
|
||||
"fullyQualifiedDomainName",
|
||||
criteriaBuilder::greaterThan,
|
||||
cursorString.get());
|
||||
"domainName", criteriaBuilder::greaterThan, cursorString.get());
|
||||
}
|
||||
replicaJpaTm()
|
||||
.criteriaQuery(queryBuilder.build())
|
||||
|
||||
@@ -223,7 +223,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
Query<Host> query =
|
||||
queryItems(
|
||||
Host.class,
|
||||
"fullyQualifiedHostName",
|
||||
"hostName",
|
||||
partialStringQuery,
|
||||
cursorString,
|
||||
getDeletedItemHandling(),
|
||||
@@ -237,7 +237,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
CriteriaQueryBuilder<Host> queryBuilder =
|
||||
queryItemsSql(
|
||||
Host.class,
|
||||
"fullyQualifiedHostName",
|
||||
"hostName",
|
||||
partialStringQuery,
|
||||
cursorString,
|
||||
getDeletedItemHandling());
|
||||
|
||||
@@ -25,7 +25,7 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.domain.secdns.DomainDsData;
|
||||
import google.registry.rdap.AbstractJsonableObject.RestrictJsonNames;
|
||||
import google.registry.rdap.RdapDataStructures.Event;
|
||||
import google.registry.rdap.RdapDataStructures.EventWithoutActor;
|
||||
@@ -444,7 +444,7 @@ final class RdapObjectClasses {
|
||||
@JsonableElement
|
||||
abstract int digestType();
|
||||
|
||||
static DsData create(DelegationSignerData dsData) {
|
||||
static DsData create(DomainDsData dsData) {
|
||||
return new AutoValue_RdapObjectClasses_SecureDns_DsData(
|
||||
dsData.getKeyTag(),
|
||||
dsData.getAlgorithm(),
|
||||
@@ -490,7 +490,7 @@ final class RdapObjectClasses {
|
||||
|
||||
abstract ImmutableList.Builder<DsData> dsDataBuilder();
|
||||
|
||||
Builder addDsData(DelegationSignerData dsData) {
|
||||
Builder addDsData(DomainDsData dsData) {
|
||||
dsDataBuilder().add(DsData.create(dsData));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.domain.secdns.DomainDsData;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.rde.RdeMode;
|
||||
import google.registry.model.transfer.DomainTransferData;
|
||||
@@ -197,7 +197,7 @@ final class DomainToXjcConverter {
|
||||
// completely useless.
|
||||
if (!model.getDsData().isEmpty()) {
|
||||
XjcSecdnsDsOrKeyType secdns = new XjcSecdnsDsOrKeyType();
|
||||
for (DelegationSignerData ds : model.getDsData()) {
|
||||
for (DomainDsData ds : model.getDsData()) {
|
||||
secdns.getDsDatas().add(convertDelegationSignerData(ds));
|
||||
}
|
||||
bean.setSecDNS(secdns);
|
||||
@@ -284,8 +284,8 @@ final class DomainToXjcConverter {
|
||||
return bean;
|
||||
}
|
||||
|
||||
/** Converts {@link DelegationSignerData} to {@link XjcSecdnsDsDataType}. */
|
||||
private static XjcSecdnsDsDataType convertDelegationSignerData(DelegationSignerData model) {
|
||||
/** Converts {@link DomainDsData} to {@link XjcSecdnsDsDataType}. */
|
||||
private static XjcSecdnsDsDataType convertDelegationSignerData(DomainDsData model) {
|
||||
XjcSecdnsDsDataType bean = new XjcSecdnsDsDataType();
|
||||
bean.setKeyTag(model.getKeyTag());
|
||||
bean.setAlg((short) model.getAlgorithm());
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
package google.registry.reporting.billing;
|
||||
|
||||
import static google.registry.beam.BeamUtils.createJobName;
|
||||
import static google.registry.model.common.Cursor.CursorType.RECURRING_BILLING;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
@@ -29,6 +32,7 @@ import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.common.Cursor;
|
||||
import google.registry.persistence.PersistenceModule;
|
||||
import google.registry.reporting.ReportingModule;
|
||||
import google.registry.request.Action;
|
||||
@@ -40,6 +44,7 @@ import google.registry.util.Clock;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import java.io.IOException;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.joda.time.YearMonth;
|
||||
|
||||
@@ -108,6 +113,19 @@ public class GenerateInvoicesAction implements Runnable {
|
||||
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
||||
logger.atInfo().log("Launching invoicing pipeline for %s.", yearMonth);
|
||||
try {
|
||||
DateTime currentCursorTime =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().loadByKeyIfPresent(Cursor.createGlobalVKey(RECURRING_BILLING))
|
||||
.orElse(Cursor.createGlobal(RECURRING_BILLING, START_OF_TIME))
|
||||
.getCursorTime());
|
||||
|
||||
if (yearMonth.getMonthOfYear() >= currentCursorTime.getMonthOfYear()) {
|
||||
throw new IllegalStateException(
|
||||
"Latest billing events expansion cycle hasn't finished yet, terminating invoicing"
|
||||
+ " pipeline");
|
||||
}
|
||||
|
||||
LaunchFlexTemplateParameter parameter =
|
||||
new LaunchFlexTemplateParameter()
|
||||
.setJobName(createJobName("invoicing", clock))
|
||||
@@ -150,7 +168,7 @@ public class GenerateInvoicesAction implements Runnable {
|
||||
}
|
||||
response.setStatus(SC_OK);
|
||||
response.setPayload(String.format("Launched invoicing pipeline: %s", jobId));
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | IllegalStateException e) {
|
||||
logger.atWarning().withCause(e).log("Template Launch failed.");
|
||||
emailUtils.sendAlertEmail(String.format("Pipeline Launch failed due to %s", e.getMessage()));
|
||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
-- Determine the number of domains each registrar sponsors per tld.
|
||||
|
||||
-- This is just the number of fullyQualifiedDomainNames under each
|
||||
-- This is just the number of domainNames under each
|
||||
-- tld-registrar pair.
|
||||
|
||||
SELECT
|
||||
|
||||
@@ -137,10 +137,7 @@ public class Spec11EmailUtils {
|
||||
threatMatch ->
|
||||
tm()
|
||||
.createQueryComposer(Domain.class)
|
||||
.where(
|
||||
"fullyQualifiedDomainName",
|
||||
Comparator.EQ,
|
||||
threatMatch.fullyQualifiedDomainName())
|
||||
.where("domainName", Comparator.EQ, threatMatch.domainName())
|
||||
.stream()
|
||||
.anyMatch(Domain::shouldPublishToDns))
|
||||
.collect(toImmutableList());
|
||||
@@ -176,7 +173,7 @@ public class Spec11EmailUtils {
|
||||
.map(
|
||||
threatMatch ->
|
||||
ImmutableMap.of(
|
||||
"fullyQualifiedDomainName", threatMatch.fullyQualifiedDomainName(),
|
||||
"domainName", threatMatch.domainName(),
|
||||
"threatType", threatMatch.threatType()))
|
||||
.collect(toImmutableList());
|
||||
|
||||
|
||||
@@ -316,7 +316,7 @@ public class AuthenticatedRegistrarAccessor {
|
||||
jpaTm()
|
||||
.query(
|
||||
"SELECT r FROM Registrar r INNER JOIN RegistrarPoc rp ON "
|
||||
+ "r.clientIdentifier = rp.registrarId WHERE rp.gaeUserId = "
|
||||
+ "r.registrarId = rp.registrarId WHERE rp.gaeUserId = "
|
||||
+ ":gaeUserId AND r.state != :state",
|
||||
Registrar.class)
|
||||
.setParameter("gaeUserId", user.getUserId())
|
||||
|
||||
@@ -19,10 +19,10 @@ import static com.google.common.base.Preconditions.checkState;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Strings;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.persistence.VKey;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -42,7 +42,7 @@ class CommandUtilities {
|
||||
}
|
||||
|
||||
public VKey<? extends EppResource> getKey(String uniqueId, DateTime now) {
|
||||
return ForeignKeyIndex.loadAndGetKey(clazz, uniqueId, now);
|
||||
return ForeignKeyUtils.load(clazz, uniqueId, now);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.domain.token.PackagePromotion;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Shared base class for commands to create or update a PackagePromotion object. */
|
||||
abstract class CreateOrUpdatePackagePromotionCommand extends MutatingCommand {
|
||||
|
||||
@Parameter(description = "Allocation token String of the package token", required = true)
|
||||
List<String> mainParameters;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"-d", "--max_domains"},
|
||||
description = "Maximum concurrent active domains allowed in the package")
|
||||
Integer maxDomains;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"-c", "--max_creates"},
|
||||
description = "Maximum domain creations allowed in the package each year")
|
||||
Integer maxCreates;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"-p", "--price"},
|
||||
description = "Annual price of the package")
|
||||
Money price;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--next_billing_date",
|
||||
description = "The next date that the package should be billed for its annual fee")
|
||||
Date nextBillingDate;
|
||||
|
||||
/** Returns the existing PackagePromotion or null if it does not exist. */
|
||||
@Nullable
|
||||
abstract PackagePromotion getOldPackagePromotion(String token);
|
||||
|
||||
/** Returns the allocation token object. */
|
||||
AllocationToken getAndCheckAllocationToken(String token) {
|
||||
Optional<AllocationToken> allocationToken =
|
||||
tm().transact(() -> tm().loadByKeyIfPresent(VKey.createSql(AllocationToken.class, token)));
|
||||
checkArgument(
|
||||
allocationToken.isPresent(),
|
||||
"An allocation token with the token String %s does not exist. The package token must be"
|
||||
+ " created first before it can be used to create a PackagePromotion",
|
||||
token);
|
||||
checkArgument(
|
||||
allocationToken.get().getTokenType().equals(TokenType.PACKAGE),
|
||||
"The allocation token must be of the PACKAGE token type");
|
||||
return allocationToken.get();
|
||||
}
|
||||
|
||||
/** Does not clear the lastNotificationSent field. Subclasses can override this. */
|
||||
boolean clearLastNotificationSent() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void init() throws Exception {
|
||||
for (String token : mainParameters) {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
PackagePromotion oldPackage = getOldPackagePromotion(token);
|
||||
checkArgument(
|
||||
oldPackage != null || price != null,
|
||||
"PackagePrice is required when creating a new package");
|
||||
|
||||
AllocationToken allocationToken = getAndCheckAllocationToken(token);
|
||||
|
||||
PackagePromotion.Builder builder =
|
||||
(oldPackage == null)
|
||||
? new PackagePromotion.Builder().setToken(allocationToken)
|
||||
: oldPackage.asBuilder();
|
||||
|
||||
Optional.ofNullable(maxDomains).ifPresent(builder::setMaxDomains);
|
||||
Optional.ofNullable(maxCreates).ifPresent(builder::setMaxCreates);
|
||||
Optional.ofNullable(price).ifPresent(builder::setPackagePrice);
|
||||
Optional.ofNullable(nextBillingDate)
|
||||
.ifPresent(
|
||||
nextBillingDate ->
|
||||
builder.setNextBillingDate(new DateTime(nextBillingDate)));
|
||||
if (clearLastNotificationSent()) {
|
||||
builder.setLastNotificationSent(null);
|
||||
}
|
||||
PackagePromotion newPackage = builder.build();
|
||||
stageEntityChange(oldPackage, newPackage);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.domain.token.PackagePromotion;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/** Command to create a PackagePromotion */
|
||||
@Parameters(
|
||||
separators = " =",
|
||||
commandDescription =
|
||||
"Create new package promotion object(s) for registrars to register multiple names under"
|
||||
+ " one contractual annual package price using a package allocation token")
|
||||
public final class CreatePackagePromotionCommand extends CreateOrUpdatePackagePromotionCommand {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
PackagePromotion getOldPackagePromotion(String tokenString) {
|
||||
checkArgument(
|
||||
!PackagePromotion.loadByTokenString(tokenString).isPresent(),
|
||||
"PackagePromotion with token %s already exists",
|
||||
tokenString);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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.tools;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.domain.token.PackagePromotion;
|
||||
import java.util.List;
|
||||
|
||||
/** Command to show a {@link PackagePromotion} object. */
|
||||
@Parameters(separators = " =", commandDescription = "Show package promotion object(s)")
|
||||
public class GetPackagePromotionCommand extends GetEppResourceCommand {
|
||||
|
||||
@Parameter(description = "Package token(s)", required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Override
|
||||
void runAndPrint() {
|
||||
for (String token : mainParameters) {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
PackagePromotion packagePromotion =
|
||||
checkArgumentPresent(
|
||||
PackagePromotion.loadByTokenString(token),
|
||||
"PackagePromotion with package token %s does not exist",
|
||||
token);
|
||||
System.out.println(packagePromotion);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ package google.registry.tools;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.tools.javascrap.CompareEscrowDepositsCommand;
|
||||
import google.registry.tools.javascrap.CreateCancellationsForOneTimesCommand;
|
||||
|
||||
/** Container class to create and run remote commands against a Datastore instance. */
|
||||
public final class RegistryTool {
|
||||
@@ -36,10 +37,12 @@ public final class RegistryTool {
|
||||
.put("convert_idn", ConvertIdnCommand.class)
|
||||
.put("count_domains", CountDomainsCommand.class)
|
||||
.put("create_anchor_tenant", CreateAnchorTenantCommand.class)
|
||||
.put("create_cancellations_for_one_times", CreateCancellationsForOneTimesCommand.class)
|
||||
.put("create_cdns_tld", CreateCdnsTld.class)
|
||||
.put("create_contact", CreateContactCommand.class)
|
||||
.put("create_domain", CreateDomainCommand.class)
|
||||
.put("create_host", CreateHostCommand.class)
|
||||
.put("create_package_promotion", CreatePackagePromotionCommand.class)
|
||||
.put("create_premium_list", CreatePremiumListCommand.class)
|
||||
.put("create_registrar", CreateRegistrarCommand.class)
|
||||
.put("create_registrar_groups", CreateRegistrarGroupsCommand.class)
|
||||
@@ -68,6 +71,7 @@ public final class RegistryTool {
|
||||
.put("get_history_entries", GetHistoryEntriesCommand.class)
|
||||
.put("get_host", GetHostCommand.class)
|
||||
.put("get_keyring_secret", GetKeyringSecretCommand.class)
|
||||
.put("get_package_promotion", GetPackagePromotionCommand.class)
|
||||
.put("get_premium_list", GetPremiumListCommand.class)
|
||||
.put("get_registrar", GetRegistrarCommand.class)
|
||||
.put("get_reserved_list", GetReservedListCommand.class)
|
||||
@@ -104,6 +108,7 @@ public final class RegistryTool {
|
||||
.put("update_cursors", UpdateCursorsCommand.class)
|
||||
.put("update_domain", UpdateDomainCommand.class)
|
||||
.put("update_keyring_secret", UpdateKeyringSecretCommand.class)
|
||||
.put("update_package_promotion", UpdatePackagePromotionCommand.class)
|
||||
.put("update_premium_list", UpdatePremiumListCommand.class)
|
||||
.put("update_registrar", UpdateRegistrarCommand.class)
|
||||
.put("update_reserved_list", UpdateReservedListCommand.class)
|
||||
|
||||
@@ -42,6 +42,7 @@ import google.registry.request.Modules.UrlFetchServiceModule;
|
||||
import google.registry.request.Modules.UserServiceModule;
|
||||
import google.registry.tools.AuthModule.LocalCredentialModule;
|
||||
import google.registry.tools.javascrap.CompareEscrowDepositsCommand;
|
||||
import google.registry.tools.javascrap.CreateCancellationsForOneTimesCommand;
|
||||
import google.registry.util.UtilsModule;
|
||||
import google.registry.whois.NonCachingWhoisModule;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -95,6 +96,8 @@ interface RegistryToolComponent {
|
||||
|
||||
void inject(CreateAnchorTenantCommand command);
|
||||
|
||||
void inject(CreateCancellationsForOneTimesCommand command);
|
||||
|
||||
void inject(CreateCdnsTld command);
|
||||
|
||||
void inject(CreateContactCommand command);
|
||||
|
||||
@@ -33,7 +33,7 @@ import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.template.soy.data.SoyListData;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.domain.secdns.DomainDsData;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.tools.soy.DomainRenewSoyInfo;
|
||||
@@ -232,7 +232,7 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
|
||||
private ImmutableList<ImmutableMap<String, Object>> getExistingDsData(Domain domain) {
|
||||
ImmutableList.Builder<ImmutableMap<String, Object>> dsDataJsons = new ImmutableList.Builder();
|
||||
HexBinaryAdapter hexBinaryAdapter = new HexBinaryAdapter();
|
||||
for (DelegationSignerData dsData : domain.getDsData()) {
|
||||
for (DomainDsData dsData : domain.getDsData()) {
|
||||
dsDataJsons.add(
|
||||
ImmutableMap.of(
|
||||
"keyTag", dsData.getKeyTag(),
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.Domain;
|
||||
@@ -38,7 +39,6 @@ import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.Period.Unit;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.HistoryEntry.Type;
|
||||
import google.registry.util.Clock;
|
||||
@@ -88,7 +88,7 @@ class UnrenewDomainCommand extends ConfirmingCommand implements CommandWithRemot
|
||||
new ImmutableMap.Builder<>();
|
||||
|
||||
for (String domainName : mainParameters) {
|
||||
if (ForeignKeyIndex.load(Domain.class, domainName, START_OF_TIME) == null) {
|
||||
if (ForeignKeyUtils.load(Domain.class, domainName, START_OF_TIME) == null) {
|
||||
domainsNonexistentBuilder.add(domainName);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -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.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.domain.token.PackagePromotion;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Command to update a PackagePromotion */
|
||||
@Parameters(separators = " =", commandDescription = "Update package promotion object(s)")
|
||||
public final class UpdatePackagePromotionCommand extends CreateOrUpdatePackagePromotionCommand {
|
||||
|
||||
@Parameter(
|
||||
names = "--clear_last_notification_sent",
|
||||
description =
|
||||
"Clear the date last max-domain non-compliance notification was sent? (This should be"
|
||||
+ " cleared whenever a tier is upgraded)")
|
||||
boolean clearLastNotificationSent;
|
||||
|
||||
@Override
|
||||
PackagePromotion getOldPackagePromotion(String token) {
|
||||
Optional<PackagePromotion> oldPackage = PackagePromotion.loadByTokenString(token);
|
||||
checkArgument(oldPackage.isPresent(), "PackagePromotion with token %s does not exist", token);
|
||||
return oldPackage.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean clearLastNotificationSent() {
|
||||
return clearLastNotificationSent;
|
||||
}
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
// 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.tools.javascrap;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Cancellation;
|
||||
import google.registry.model.billing.BillingEvent.OneTime;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.QueryComposer.Comparator;
|
||||
import google.registry.tools.CommandWithRemoteApi;
|
||||
import google.registry.tools.ConfirmingCommand;
|
||||
import google.registry.tools.params.LongParameter;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command to create {@link Cancellation}s for specified {@link OneTime} billing events.
|
||||
*
|
||||
* <p>This can be used to fix situations where we've inadvertently billed registrars. It's generally
|
||||
* easier and better to issue cancellations within the Nomulus system than to attempt to issue
|
||||
* refunds after the fact.
|
||||
*/
|
||||
@Parameters(separators = " =", commandDescription = "Manually create Cancellations for OneTimes.")
|
||||
public class CreateCancellationsForOneTimesCommand extends ConfirmingCommand
|
||||
implements CommandWithRemoteApi {
|
||||
|
||||
@Parameter(
|
||||
description = "Space-delimited billing event ID(s) to cancel",
|
||||
required = true,
|
||||
validateWith = LongParameter.class)
|
||||
private List<Long> mainParameters;
|
||||
|
||||
private ImmutableSet<OneTime> oneTimesToCancel;
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
ImmutableSet.Builder<Long> missingIdsBuilder = new ImmutableSet.Builder<>();
|
||||
ImmutableSet.Builder<Long> alreadyCancelledIdsBuilder = new ImmutableSet.Builder<>();
|
||||
ImmutableSet.Builder<OneTime> oneTimesBuilder = new ImmutableSet.Builder<>();
|
||||
tm().transact(
|
||||
() -> {
|
||||
for (Long billingEventId : ImmutableSet.copyOf(mainParameters)) {
|
||||
VKey<OneTime> key = VKey.createSql(OneTime.class, billingEventId);
|
||||
if (tm().exists(key)) {
|
||||
OneTime oneTime = tm().loadByKey(key);
|
||||
if (alreadyCancelled(oneTime)) {
|
||||
alreadyCancelledIdsBuilder.add(billingEventId);
|
||||
} else {
|
||||
oneTimesBuilder.add(oneTime);
|
||||
}
|
||||
} else {
|
||||
missingIdsBuilder.add(billingEventId);
|
||||
}
|
||||
}
|
||||
});
|
||||
oneTimesToCancel = oneTimesBuilder.build();
|
||||
System.out.printf("Found %d OneTime(s) to cancel\n", oneTimesToCancel.size());
|
||||
ImmutableSet<Long> missingIds = missingIdsBuilder.build();
|
||||
if (!missingIds.isEmpty()) {
|
||||
System.out.printf("Missing OneTime(s) for IDs %s\n", missingIds);
|
||||
}
|
||||
ImmutableSet<Long> alreadyCancelledIds = alreadyCancelledIdsBuilder.build();
|
||||
if (!alreadyCancelledIds.isEmpty()) {
|
||||
System.out.printf(
|
||||
"The following OneTime IDs were already cancelled: %s\n", alreadyCancelledIds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String prompt() {
|
||||
return String.format("Create cancellations for %d OneTime(s)?", oneTimesToCancel.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute() throws Exception {
|
||||
int cancelledOneTimes = 0;
|
||||
for (OneTime oneTime : oneTimesToCancel) {
|
||||
cancelledOneTimes +=
|
||||
tm().transact(
|
||||
() -> {
|
||||
if (alreadyCancelled(oneTime)) {
|
||||
System.out.printf(
|
||||
"OneTime %d already cancelled, this is unexpected.\n", oneTime.getId());
|
||||
return 0;
|
||||
}
|
||||
tm().put(
|
||||
new Cancellation.Builder()
|
||||
.setOneTimeEventKey(oneTime.createVKey())
|
||||
.setBillingTime(oneTime.getBillingTime())
|
||||
.setDomainHistoryId(oneTime.getDomainHistoryId())
|
||||
.setRegistrarId(oneTime.getRegistrarId())
|
||||
.setEventTime(oneTime.getEventTime())
|
||||
.setReason(BillingEvent.Reason.ERROR)
|
||||
.setTargetId(oneTime.getTargetId())
|
||||
.build());
|
||||
System.out.printf(
|
||||
"Added Cancellation for OneTime with ID %d\n", oneTime.getId());
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
return String.format("Created %d Cancellation event(s)", cancelledOneTimes);
|
||||
}
|
||||
|
||||
private boolean alreadyCancelled(OneTime oneTime) {
|
||||
return tm().createQueryComposer(Cancellation.class)
|
||||
.where("refOneTime", Comparator.EQ, oneTime.getId())
|
||||
.first()
|
||||
.isPresent();
|
||||
}
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
// 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.tools.javascrap;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import dagger.Component;
|
||||
import google.registry.beam.common.RegistryJpaIO;
|
||||
import google.registry.beam.common.RegistryPipelineOptions;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.io.Serializable;
|
||||
import javax.inject.Singleton;
|
||||
import org.apache.beam.sdk.Pipeline;
|
||||
import org.apache.beam.sdk.options.PipelineOptions;
|
||||
import org.apache.beam.sdk.options.PipelineOptionsFactory;
|
||||
import org.apache.beam.sdk.transforms.DoFn;
|
||||
import org.apache.beam.sdk.transforms.ParDo;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Pipeline that creates a synthetic history for every non-deleted {@link Domain} in SQL.
|
||||
*
|
||||
* <p>This is created to fix the issue identified in b/248112997. After b/245940594, there were some
|
||||
* domains where the most recent history object did not represent the state of the domain as it
|
||||
* exists in the world. Because RDE loads only from DomainHistory objects, this means that RDE was
|
||||
* producing wrong data. This pipeline mitigates that issue by creating synthetic history events for
|
||||
* every domain that was not deleted as of the start of the pipeline -- then, we can guarantee that
|
||||
* this new history object represents the state of the domain as far as we know.
|
||||
*
|
||||
* <p>To run the pipeline (replace the environment as appropriate):
|
||||
*
|
||||
* <p><code>
|
||||
* $ ./nom_build :core:createSyntheticDomainHistories --args="--region=us-central1
|
||||
* --runner=DataflowRunner
|
||||
* --registryEnvironment=CRASH
|
||||
* --project={project-id}
|
||||
* --workerMachineType=n2-standard-4"
|
||||
* </code>
|
||||
*/
|
||||
public class CreateSyntheticDomainHistoriesPipeline implements Serializable {
|
||||
|
||||
private static final String HISTORY_REASON =
|
||||
"Create synthetic domain histories to fix RDE for b/248112997";
|
||||
private static final DateTime BAD_PIPELINE_START_TIME =
|
||||
DateTime.parse("2022-09-05T09:00:00.000Z");
|
||||
private static final DateTime BAD_PIPELINE_END_TIME = DateTime.parse("2022-09-10T12:00:00.000Z");
|
||||
|
||||
static void setup(Pipeline pipeline, String registryAdminRegistrarId) {
|
||||
pipeline
|
||||
.apply(
|
||||
"Read all domain repo IDs",
|
||||
RegistryJpaIO.read(
|
||||
"SELECT d.repoId FROM Domain d WHERE deletionTime > :badPipelineStartTime AND NOT"
|
||||
+ " EXISTS (SELECT 1 FROM DomainHistory dh WHERE dh.domainRepoId = d.repoId"
|
||||
+ " AND dh.modificationTime > :badPipelineEndTime)",
|
||||
ImmutableMap.of(
|
||||
"badPipelineStartTime",
|
||||
BAD_PIPELINE_START_TIME,
|
||||
"badPipelineEndTime",
|
||||
BAD_PIPELINE_END_TIME),
|
||||
String.class,
|
||||
repoId -> VKey.createSql(Domain.class, repoId)))
|
||||
.apply(
|
||||
"Save a synthetic DomainHistory for each domain",
|
||||
ParDo.of(new DomainHistoryCreator(registryAdminRegistrarId)));
|
||||
}
|
||||
|
||||
private static class DomainHistoryCreator extends DoFn<VKey<Domain>, Void> {
|
||||
|
||||
private final String registryAdminRegistrarId;
|
||||
|
||||
private DomainHistoryCreator(String registryAdminRegistrarId) {
|
||||
this.registryAdminRegistrarId = registryAdminRegistrarId;
|
||||
}
|
||||
|
||||
@ProcessElement
|
||||
public void processElement(
|
||||
@Element VKey<Domain> key, PipelineOptions options, OutputReceiver<Void> outputReceiver) {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
Domain domain = jpaTm().loadByKey(key);
|
||||
jpaTm()
|
||||
.put(
|
||||
HistoryEntry.createBuilderForResource(domain)
|
||||
.setRegistrarId(registryAdminRegistrarId)
|
||||
.setBySuperuser(true)
|
||||
.setRequestedByRegistrar(false)
|
||||
.setModificationTime(jpaTm().getTransactionTime())
|
||||
.setReason(HISTORY_REASON)
|
||||
.setType(HistoryEntry.Type.SYNTHETIC)
|
||||
.build());
|
||||
outputReceiver.output(null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
RegistryPipelineOptions options =
|
||||
PipelineOptionsFactory.fromArgs(args).withValidation().as(RegistryPipelineOptions.class);
|
||||
RegistryPipelineOptions.validateRegistryPipelineOptions(options);
|
||||
options.setIsolationOverride(TransactionIsolationLevel.TRANSACTION_READ_COMMITTED);
|
||||
String registryAdminRegistrarId =
|
||||
DaggerCreateSyntheticDomainHistoriesPipeline_ConfigComponent.create()
|
||||
.getRegistryAdminRegistrarId();
|
||||
|
||||
Pipeline pipeline = Pipeline.create(options);
|
||||
setup(pipeline, registryAdminRegistrarId);
|
||||
pipeline.run();
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Component(modules = ConfigModule.class)
|
||||
interface ConfigComponent {
|
||||
|
||||
@Config("registryAdminClientId")
|
||||
String getRegistryAdminRegistrarId();
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ import com.google.common.flogger.FluentLogger;
|
||||
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.domain.secdns.DomainDsData;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.HttpException.BadRequestException;
|
||||
@@ -241,7 +241,7 @@ public class GenerateZoneFilesAction implements Runnable, JsonActionRunner.JsonA
|
||||
// Load the nameservers at the export time in case they've been renamed or deleted.
|
||||
loadAtPointInTime(nameserver, exportTime).getHostName()));
|
||||
}
|
||||
for (DelegationSignerData dsData : domain.getDsData()) {
|
||||
for (DomainDsData dsData : domain.getDsData()) {
|
||||
result.append(
|
||||
String.format(
|
||||
DS_FORMAT,
|
||||
|
||||
@@ -69,7 +69,7 @@ public final class ListDomainsAction extends ListObjectsAction<Domain> {
|
||||
|
||||
@Override
|
||||
public ImmutableSet<String> getPrimaryKeyFields() {
|
||||
return ImmutableSet.of("fullyQualifiedDomainName");
|
||||
return ImmutableSet.of("domainName");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -44,7 +44,7 @@ public final class ListHostsAction extends ListObjectsAction<Host> {
|
||||
|
||||
@Override
|
||||
public ImmutableSet<String> getPrimaryKeyFields() {
|
||||
return ImmutableSet.of("fullyQualifiedHostName");
|
||||
return ImmutableSet.of("hostName");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,7 +39,7 @@ public final class ListRegistrarsAction extends ListObjectsAction<Registrar> {
|
||||
|
||||
@Override
|
||||
public ImmutableSet<String> getPrimaryKeyFields() {
|
||||
return ImmutableSet.of("clientIdentifier");
|
||||
return ImmutableSet.of("registrarId");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,7 +51,7 @@ public final class ListRegistrarsAction extends ListObjectsAction<Registrar> {
|
||||
public ImmutableBiMap<String, String> getFieldAliases() {
|
||||
return ImmutableBiMap.of(
|
||||
"billingId", "billingIdentifier",
|
||||
"clientId", "clientIdentifier",
|
||||
"clientId", "registrarId",
|
||||
"premiumNames", "blockPremiumNames");
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
() ->
|
||||
jpaTm()
|
||||
.query(
|
||||
"SELECT fullyQualifiedDomainName FROM Domain "
|
||||
"SELECT domainName FROM Domain "
|
||||
+ "WHERE tld IN (:tlds) "
|
||||
+ "AND deletionTime > :now",
|
||||
String.class)
|
||||
|
||||
@@ -132,7 +132,7 @@ registry.json.Response.prototype.results;
|
||||
/**
|
||||
* @typedef {{
|
||||
* allowedTlds: !Array<string>,
|
||||
* clientIdentifier: string,
|
||||
* registrarId: string,
|
||||
* clientCertificate: string?,
|
||||
* clientCertificateHash: string?,
|
||||
* failoverClientCertificate: string?,
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<class>google.registry.model.domain.DomainHistory</class>
|
||||
<class>google.registry.model.domain.GracePeriod</class>
|
||||
<class>google.registry.model.domain.GracePeriod$GracePeriodHistory</class>
|
||||
<class>google.registry.model.domain.secdns.DelegationSignerData</class>
|
||||
<class>google.registry.model.domain.secdns.DomainDsData</class>
|
||||
<class>google.registry.model.domain.secdns.DomainDsDataHistory</class>
|
||||
<class>google.registry.model.domain.token.AllocationToken</class>
|
||||
<class>google.registry.model.domain.token.PackagePromotion</class>
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@
|
||||
-- query's performance and consider switching to using a native query.
|
||||
|
||||
SELECT b, r FROM BillingEvent b
|
||||
JOIN Registrar r ON b.clientId = r.clientIdentifier
|
||||
JOIN Registrar r ON b.clientId = r.registrarId
|
||||
JOIN Domain d ON b.domainRepoId = d.repoId
|
||||
JOIN Tld t ON t.tldStr = d.tld
|
||||
LEFT JOIN BillingCancellation c ON b.id = c.refOneTime
|
||||
|
||||
@@ -19,14 +19,14 @@
|
||||
-- email address.
|
||||
|
||||
SELECT
|
||||
domain.fullyQualifiedDomainName AS domainName,
|
||||
domain.domainName AS domainName,
|
||||
domain.__key__.name AS domainRepoId,
|
||||
registrar.clientId AS registrarId,
|
||||
COALESCE(registrar.emailAddress, '') AS registrarEmailAddress
|
||||
FROM ( (
|
||||
SELECT
|
||||
__key__,
|
||||
fullyQualifiedDomainName,
|
||||
domainName,
|
||||
currentSponsorClientId,
|
||||
creationTime
|
||||
FROM
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
</tr>
|
||||
{for $threat in $threats}
|
||||
<tr>
|
||||
<td>{$threat['fullyQualifiedDomainName']}</td>
|
||||
<td>{$threat['domainName']}</td>
|
||||
<td>{$threat['threatType']}</td>
|
||||
</tr>
|
||||
{/for}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
/** Registrar whois settings page for view and edit. */
|
||||
{template .settings}
|
||||
{@param clientIdentifier: string}
|
||||
{@param registrarId: string}
|
||||
{@param? ianaIdentifier: int}
|
||||
{@param? icannReferralEmail: string}
|
||||
{@param readonly: bool}
|
||||
@@ -40,8 +40,8 @@
|
||||
<table>
|
||||
{call registry.soy.forms.inputFieldRowWithValue}
|
||||
{param label: 'Name' /}
|
||||
{param name: 'clientIdentifier' /}
|
||||
{param value: $clientIdentifier /}
|
||||
{param name: 'registrarId' /}
|
||||
{param value: $registrarId /}
|
||||
{param readonly: true /}
|
||||
{/call}
|
||||
{call registry.soy.forms.inputFieldRowWithValue}
|
||||
|
||||
@@ -98,7 +98,7 @@ public class ResaveEntityActionTest {
|
||||
clock.advanceOneMilli();
|
||||
assertThat(domain.getCurrentSponsorRegistrarId()).isEqualTo("TheRegistrar");
|
||||
runAction(
|
||||
domain.createVKey().getOfyKey().getString(),
|
||||
domain.createVKey().stringify(),
|
||||
DateTime.parse("2016-02-06T10:00:01Z"),
|
||||
ImmutableSortedSet.of());
|
||||
Domain resavedDomain = loadByEntity(domain);
|
||||
@@ -128,7 +128,7 @@ public class ResaveEntityActionTest {
|
||||
|
||||
assertThat(domain.getGracePeriods()).isNotEmpty();
|
||||
runAction(
|
||||
domain.createVKey().getOfyKey().getString(),
|
||||
domain.createVKey().stringify(),
|
||||
requestedTime,
|
||||
ImmutableSortedSet.of(requestedTime.plusDays(5)));
|
||||
Domain resavedDomain = loadByEntity(domain);
|
||||
|
||||
@@ -33,7 +33,7 @@ import google.registry.model.domain.DomainAuthInfo;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
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.secdns.DomainDsData;
|
||||
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
@@ -110,7 +110,7 @@ public class RegistryJpaReadTest {
|
||||
Read<Object[], String> read =
|
||||
RegistryJpaIO.read(
|
||||
"select d, r.emailAddress from Domain d join Registrar r on"
|
||||
+ " d.currentSponsorClientId = r.clientIdentifier where r.type = :type"
|
||||
+ " d.currentSponsorClientId = r.registrarId where r.type = :type"
|
||||
+ " and d.deletionTime > now()",
|
||||
ImmutableMap.of("type", Registrar.Type.REAL),
|
||||
false,
|
||||
@@ -152,7 +152,7 @@ public class RegistryJpaReadTest {
|
||||
Read<Domain, String> read =
|
||||
RegistryJpaIO.read(
|
||||
"select d from Domain d join Registrar r on"
|
||||
+ " d.currentSponsorClientId = r.clientIdentifier where r.type = :type"
|
||||
+ " d.currentSponsorClientId = r.registrarId where r.type = :type"
|
||||
+ " and d.deletionTime > now()",
|
||||
ImmutableMap.of("type", Registrar.Type.REAL),
|
||||
Domain.class,
|
||||
@@ -200,7 +200,7 @@ public class RegistryJpaReadTest {
|
||||
.setPersistedCurrentSponsorRegistrarId(registrar.getRegistrarId())
|
||||
.setRegistrationExpirationTime(fakeClock.nowUtc().plusYears(1))
|
||||
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("password")))
|
||||
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2})))
|
||||
.setDsData(ImmutableSet.of(DomainDsData.create(1, 2, 3, new byte[] {0, 1, 2})))
|
||||
.setLaunchNotice(
|
||||
LaunchNotice.create("tcnid", "validatorId", START_OF_TIME, START_OF_TIME))
|
||||
.setSmdId("smdid")
|
||||
|
||||
@@ -355,7 +355,7 @@ class InvoicingPipelineTest {
|
||||
.isEqualTo(
|
||||
'\n'
|
||||
+ "SELECT b, r FROM BillingEvent b\n"
|
||||
+ "JOIN Registrar r ON b.clientId = r.clientIdentifier\n"
|
||||
+ "JOIN Registrar r ON b.clientId = r.registrarId\n"
|
||||
+ "JOIN Domain d ON b.domainRepoId = d.repoId\n"
|
||||
+ "JOIN Tld t ON t.tldStr = d.tld\n"
|
||||
+ "LEFT JOIN BillingCancellation c ON b.id = c.refOneTime\n"
|
||||
|
||||
@@ -104,7 +104,7 @@ public class PublishDnsUpdatesActionTest {
|
||||
persistActiveSubordinateHost("ns1.example.xn--q9jyb4c", domain1);
|
||||
persistActiveSubordinateHost("ns2.example.xn--q9jyb4c", domain1);
|
||||
Domain domain2 = persistActiveDomain("example2.xn--q9jyb4c");
|
||||
persistActiveSubordinateHost("ns1.example.xn--q9jyb4c", domain2);
|
||||
persistActiveSubordinateHost("ns1.example2.xn--q9jyb4c", domain2);
|
||||
clock.advanceOneMilli();
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ import com.google.common.net.InetAddresses;
|
||||
import com.google.common.util.concurrent.RateLimiter;
|
||||
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.domain.secdns.DomainDsData;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
@@ -267,7 +267,7 @@ public class CloudDnsWriterTest {
|
||||
|
||||
for (int i = 0; i < dsRecords; i++) {
|
||||
dsRecordData.add(
|
||||
DelegationSignerData.create(i, 3, 1, base16().decode("1234567890ABCDEF")).toRrData());
|
||||
DomainDsData.create(i, 3, 1, base16().decode("1234567890ABCDEF")).toRrData());
|
||||
}
|
||||
recordSetBuilder.add(
|
||||
new ResourceRecordSet()
|
||||
@@ -284,10 +284,10 @@ public class CloudDnsWriterTest {
|
||||
/** Returns a domain to be persisted in Datastore. */
|
||||
private static Domain fakeDomain(
|
||||
String domainName, ImmutableSet<Host> nameservers, int numDsRecords) {
|
||||
ImmutableSet.Builder<DelegationSignerData> dsDataBuilder = new ImmutableSet.Builder<>();
|
||||
ImmutableSet.Builder<DomainDsData> dsDataBuilder = new ImmutableSet.Builder<>();
|
||||
|
||||
for (int i = 0; i < numDsRecords; i++) {
|
||||
dsDataBuilder.add(DelegationSignerData.create(i, 3, 1, base16().decode("1234567890ABCDEF")));
|
||||
dsDataBuilder.add(DomainDsData.create(i, 3, 1, base16().decode("1234567890ABCDEF")));
|
||||
}
|
||||
|
||||
ImmutableSet.Builder<VKey<Host>> hostRefBuilder = new ImmutableSet.Builder<>();
|
||||
|
||||
@@ -36,7 +36,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.net.InetAddresses;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.domain.secdns.DomainDsData;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
@@ -177,8 +177,7 @@ public class DnsUpdateWriterTest {
|
||||
.asBuilder()
|
||||
.setNameservers(ImmutableSet.of(persistActiveHost("ns1.example.tld").createVKey()))
|
||||
.setDsData(
|
||||
ImmutableSet.of(
|
||||
DelegationSignerData.create(1, 3, 1, base16().decode("0123456789ABCDEF"))))
|
||||
ImmutableSet.of(DomainDsData.create(1, 3, 1, base16().decode("0123456789ABCDEF"))))
|
||||
.build();
|
||||
persistResource(domain);
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ public class SyncRegistrarsSheetTest {
|
||||
assertThat(rows).hasSize(2);
|
||||
|
||||
ImmutableMap<String, String> row = rows.get(0);
|
||||
assertThat(row).containsEntry("clientIdentifier", "aaaregistrar");
|
||||
assertThat(row).containsEntry("registrarId", "aaaregistrar");
|
||||
assertThat(row).containsEntry("registrarName", "AAA Registrar Inc.");
|
||||
assertThat(row).containsEntry("state", "SUSPENDED");
|
||||
assertThat(row).containsEntry("ianaIdentifier", "8");
|
||||
@@ -288,7 +288,7 @@ public class SyncRegistrarsSheetTest {
|
||||
assertThat(row).containsEntry("billingAccountMap", "{JPY=JPY7890, USD=USD1234}");
|
||||
|
||||
row = rows.get(1);
|
||||
assertThat(row).containsEntry("clientIdentifier", "anotherregistrar");
|
||||
assertThat(row).containsEntry("registrarId", "anotherregistrar");
|
||||
assertThat(row).containsEntry("registrarName", "Another Registrar LLC");
|
||||
assertThat(row).containsEntry("state", "ACTIVE");
|
||||
assertThat(row).containsEntry("ianaIdentifier", "1");
|
||||
@@ -337,7 +337,7 @@ public class SyncRegistrarsSheetTest {
|
||||
|
||||
verify(sheetSynchronizer).synchronize(eq("foobar"), rowsCaptor.capture());
|
||||
ImmutableMap<String, String> row = getOnlyElement(getOnlyElement(rowsCaptor.getAllValues()));
|
||||
assertThat(row).containsEntry("clientIdentifier", "SomeRegistrar");
|
||||
assertThat(row).containsEntry("registrarId", "SomeRegistrar");
|
||||
assertThat(row).containsEntry("registrarName", "Some Registrar");
|
||||
assertThat(row).containsEntry("state", "ACTIVE");
|
||||
assertThat(row).containsEntry("ianaIdentifier", "8");
|
||||
|
||||
@@ -52,7 +52,6 @@ class ContactUpdateFlowTest extends ResourceFlowTestCase<ContactUpdateFlow, Cont
|
||||
}
|
||||
|
||||
private void doSuccessfulTest() throws Exception {
|
||||
persistActiveContact(getUniqueIdFromCommand());
|
||||
clock.advanceOneMilli();
|
||||
assertTransactionalFlow(true);
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
@@ -83,6 +82,7 @@ class ContactUpdateFlowTest extends ResourceFlowTestCase<ContactUpdateFlow, Cont
|
||||
|
||||
@Test
|
||||
void testSuccess() throws Exception {
|
||||
persistActiveContact(getUniqueIdFromCommand());
|
||||
doSuccessfulTest();
|
||||
}
|
||||
|
||||
@@ -403,6 +403,7 @@ class ContactUpdateFlowTest extends ResourceFlowTestCase<ContactUpdateFlow, Cont
|
||||
@Test
|
||||
void testSuccess_nonAsciiInLocAddress() throws Exception {
|
||||
setEppInput("contact_update_hebrew_loc.xml");
|
||||
persistActiveContact(getUniqueIdFromCommand());
|
||||
doSuccessfulTest();
|
||||
}
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ import google.registry.model.domain.fee.BaseFee.FeeType;
|
||||
import google.registry.model.domain.fee.Fee;
|
||||
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.secdns.DomainDsData;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
@@ -860,7 +860,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertAboutDomains()
|
||||
.that(domain)
|
||||
.hasExactlyDsData(
|
||||
DelegationSignerData.create(
|
||||
DomainDsData.create(
|
||||
12345, 3, 1, base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))
|
||||
.cloneWithDomainRepoId(domain.getRepoId()));
|
||||
}
|
||||
@@ -3159,15 +3159,14 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
|
||||
@Test
|
||||
void testFailure_packageToken_registrationTooLong() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.build());
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.build());
|
||||
persistContactsAndHosts();
|
||||
setEppInput(
|
||||
"domain_create_allocationtoken.xml",
|
||||
|
||||
@@ -64,7 +64,7 @@ import google.registry.model.domain.DomainAuthInfo;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.domain.secdns.DomainDsData;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
|
||||
@@ -335,8 +335,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
.asBuilder()
|
||||
.setDsData(
|
||||
ImmutableSet.of(
|
||||
DelegationSignerData.create(
|
||||
12345, 3, 1, base16().decode("49FD46E6C4B45C55D4AC"))))
|
||||
DomainDsData.create(12345, 3, 1, base16().decode("49FD46E6C4B45C55D4AC"))))
|
||||
.setNameservers(ImmutableSet.of(host1.createVKey(), host3.createVKey()))
|
||||
.build());
|
||||
doSuccessfulTest("domain_info_response_dsdata.xml", false);
|
||||
@@ -541,8 +540,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
null))
|
||||
.setDsData(
|
||||
ImmutableSet.of(
|
||||
DelegationSignerData.create(
|
||||
12345, 3, 1, base16().decode("49FD46E6C4B45C55D4AC"))))
|
||||
DomainDsData.create(12345, 3, 1, base16().decode("49FD46E6C4B45C55D4AC"))))
|
||||
.build());
|
||||
doSuccessfulTest("domain_info_response_dsdata_addperiod.xml", false);
|
||||
}
|
||||
|
||||
@@ -442,7 +442,6 @@ class DomainTransferApproveFlowTest
|
||||
.setLabelsToPrices(ImmutableMap.of("example", new BigDecimal("67.89")))
|
||||
.build());
|
||||
persistResource(Registry.get("tld").asBuilder().setPremiumList(pl).build());
|
||||
setupDomainWithPendingTransfer("example", "tld");
|
||||
domain = loadByEntity(domain);
|
||||
persistResource(
|
||||
loadByKey(domain.getAutorenewBillingEvent())
|
||||
@@ -489,7 +488,6 @@ class DomainTransferApproveFlowTest
|
||||
.setLabelsToPrices(ImmutableMap.of("example", new BigDecimal("67.89")))
|
||||
.build());
|
||||
persistResource(Registry.get("tld").asBuilder().setPremiumList(pl).build());
|
||||
setupDomainWithPendingTransfer("example", "tld");
|
||||
domain = loadByEntity(domain);
|
||||
persistResource(
|
||||
loadByKey(domain.getAutorenewBillingEvent())
|
||||
|
||||
@@ -170,7 +170,7 @@ class DomainTransferCancelFlowTest
|
||||
.setResponseData(
|
||||
ImmutableList.of(
|
||||
new DomainTransferResponse.Builder()
|
||||
.setFullyQualifiedDomainName(getUniqueIdFromCommand())
|
||||
.setDomainName(getUniqueIdFromCommand())
|
||||
.setTransferStatus(TransferStatus.CLIENT_CANCELLED)
|
||||
.setTransferRequestTime(TRANSFER_REQUEST_TIME)
|
||||
.setGainingRegistrarId("NewRegistrar")
|
||||
|
||||
@@ -21,6 +21,9 @@ import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.batch.AsyncTaskEnqueuer.PARAM_REQUESTED_TIME;
|
||||
import static google.registry.batch.AsyncTaskEnqueuer.PARAM_RESOURCE_KEY;
|
||||
import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS;
|
||||
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.DEFAULT;
|
||||
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.SPECIFIED;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.PACKAGE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.TRANSFER_SUCCESSFUL;
|
||||
@@ -61,6 +64,7 @@ import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.truth.Truth8;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.batch.ResaveEntityAction;
|
||||
import google.registry.flows.EppException;
|
||||
@@ -85,6 +89,7 @@ import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTok
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.MissingRemovePackageTokenOnPackageDomainException;
|
||||
import google.registry.flows.exceptions.AlreadyPendingTransferException;
|
||||
import google.registry.flows.exceptions.InvalidTransferPeriodValueException;
|
||||
import google.registry.flows.exceptions.MissingTransferRequestAuthInfoException;
|
||||
@@ -1700,13 +1705,15 @@ class DomainTransferRequestFlowTest
|
||||
.setDomainName("example.tld")
|
||||
.build());
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_request_allocation_token.xml", "domain_transfer_request_response.xml");
|
||||
"domain_transfer_request_allocation_token.xml",
|
||||
"domain_transfer_request_response.xml",
|
||||
ImmutableMap.of("TOKEN", "abc123"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_invalidAllocationToken() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
setEppInput("domain_transfer_request_allocation_token.xml");
|
||||
setEppInput("domain_transfer_request_allocation_token.xml", ImmutableMap.of("TOKEN", "abc123"));
|
||||
EppException thrown = assertThrows(InvalidAllocationTokenException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
@@ -1720,7 +1727,7 @@ class DomainTransferRequestFlowTest
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDomainName("otherdomain.tld")
|
||||
.build());
|
||||
setEppInput("domain_transfer_request_allocation_token.xml");
|
||||
setEppInput("domain_transfer_request_allocation_token.xml", ImmutableMap.of("TOKEN", "abc123"));
|
||||
EppException thrown =
|
||||
assertThrows(AllocationTokenNotValidForDomainException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
@@ -1741,7 +1748,7 @@ class DomainTransferRequestFlowTest
|
||||
.put(clock.nowUtc().plusDays(60), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
setEppInput("domain_transfer_request_allocation_token.xml");
|
||||
setEppInput("domain_transfer_request_allocation_token.xml", ImmutableMap.of("TOKEN", "abc123"));
|
||||
EppException thrown = assertThrows(AllocationTokenNotInPromotionException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
@@ -1762,7 +1769,7 @@ class DomainTransferRequestFlowTest
|
||||
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
setEppInput("domain_transfer_request_allocation_token.xml");
|
||||
setEppInput("domain_transfer_request_allocation_token.xml", ImmutableMap.of("TOKEN", "abc123"));
|
||||
EppException thrown =
|
||||
assertThrows(AllocationTokenNotValidForRegistrarException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
@@ -1784,7 +1791,7 @@ class DomainTransferRequestFlowTest
|
||||
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
setEppInput("domain_transfer_request_allocation_token.xml");
|
||||
setEppInput("domain_transfer_request_allocation_token.xml", ImmutableMap.of("TOKEN", "abc123"));
|
||||
EppException thrown = assertThrows(AllocationTokenNotValidForTldException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
@@ -1800,9 +1807,89 @@ class DomainTransferRequestFlowTest
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey))
|
||||
.build());
|
||||
setEppInput("domain_transfer_request_allocation_token.xml");
|
||||
setEppInput("domain_transfer_request_allocation_token.xml", ImmutableMap.of("TOKEN", "abc123"));
|
||||
EppException thrown =
|
||||
assertThrows(AlreadyRedeemedAllocationTokenException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailsPackageDomainInvalidAllocationToken() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("example", "tld"))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.build());
|
||||
setupDomain("example", "tld");
|
||||
persistResource(
|
||||
reloadResourceByForeignKey()
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.build());
|
||||
|
||||
setEppInput("domain_transfer_request_allocation_token.xml", ImmutableMap.of("TOKEN", "abc123"));
|
||||
assertThrows(MissingRemovePackageTokenOnPackageDomainException.class, this::runFlow);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailsToTransferPackageDomainNoRemovePackageToken() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("example", "tld"))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.build());
|
||||
setupDomain("example", "tld");
|
||||
persistResource(
|
||||
reloadResourceByForeignKey()
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.build());
|
||||
|
||||
setEppInput("domain_transfer_request.xml");
|
||||
assertThrows(MissingRemovePackageTokenOnPackageDomainException.class, this::runFlow);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccesfullyAppliesRemovePackageToken() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.build());
|
||||
domain = loadByEntity(domain);
|
||||
persistResource(
|
||||
loadByKey(domain.getAutorenewBillingEvent())
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, new BigDecimal("10.00")))
|
||||
.build());
|
||||
persistResource(
|
||||
reloadResourceByForeignKey()
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.build());
|
||||
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_request_allocation_token.xml",
|
||||
"domain_transfer_request_response.xml",
|
||||
ImmutableMap.of("TOKEN", "__REMOVEPACKAGE__"));
|
||||
Domain domain = reloadResourceByForeignKey();
|
||||
Truth8.assertThat(domain.getCurrentPackageToken()).isEmpty();
|
||||
RenewalPriceBehavior priceBehavior =
|
||||
loadByKey(domain.getAutorenewBillingEvent()).getRenewalPriceBehavior();
|
||||
assertThat(priceBehavior).isEqualTo(DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user