mirror of
https://github.com/google/nomulus
synced 2026-07-06 00:04:50 +00:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7b9c16ca3e | |||
| 1ab077d267 | |||
| ca65fbcc79 | |||
| 0cfa7f8081 | |||
| 9e31047c3a | |||
| b7c2e8fba5 | |||
| a299df3005 | |||
| a9b35c163d | |||
| 9da24d114c | |||
| 7dd5876315 | |||
| d1a259f63a | |||
| 8c5d2e9d92 | |||
| cca1306b09 | |||
| 47071b0fbb | |||
| d83565d37e | |||
| a557b3f376 | |||
| f4a49864b5 | |||
| acdecca181 | |||
| 5264ab3fc3 | |||
| a9d59e4d6e | |||
| 1d3738da27 | |||
| 82a3a49268 | |||
| 5bbad483e4 | |||
| f6e9dae58d | |||
| c4c1c72306 | |||
| 775f672f2a | |||
| 372c854268 | |||
| edbca15bf4 | |||
| 5f41adf843 | |||
| e21f64b745 | |||
| 0dee97934a | |||
| 1070173264 | |||
| b9a3c0cd96 |
@@ -207,6 +207,9 @@
|
||||
{
|
||||
"moduleLicense": "GNU Library General Public License v2.1 or later"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "GNU Lesser General Public License v3.0"
|
||||
},
|
||||
// This is just 3-clause BSD.
|
||||
{
|
||||
"moduleLicense": "Go License"
|
||||
|
||||
@@ -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'
|
||||
|
||||
+11
-8
@@ -31,10 +31,10 @@ com.github.jnr:jnr-unixsocket:0.38.17=compileClasspath,default,deploy_jar,nonpro
|
||||
com.github.jnr:jnr-x86asm:1.0.2=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.android:annotations:4.1.1.4=default,deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api-client:google-api-client-appengine:1.35.2=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api-client:google-api-client-appengine:1.32.2=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api-client:google-api-client-jackson2:1.32.2=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api-client:google-api-client-java6:1.35.2=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api-client:google-api-client-servlet:1.35.2=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api-client:google-api-client-servlet:1.32.2=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api-client:google-api-client:1.35.2=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:2.12.2=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.136.2=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -90,12 +90,15 @@ com.google.apis:google-api-services-pubsub:v1-rev20211130-1.32.1=compileClasspat
|
||||
com.google.apis:google-api-services-sheets:v4-rev20220620-1.32.1=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-sqladmin:v1beta4-rev20220623-1.32.1=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-storage:v1-rev20220705-1.32.1=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.appengine.tools:appengine-gcs-client:0.8.1=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.appengine.tools:appengine-gcs-client:0.8.3=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.appengine.tools:appengine-pipeline:0.2.13=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.appengine:appengine-api-1.0-sdk:2.0.5=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.appengine:appengine-api-stubs:2.0.5=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.appengine:appengine-remote-api:2.0.5=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.appengine:appengine-testing:1.9.86=default,deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.appengine:appengine-api-1.0-sdk:2.0.5=compileClasspath,nonprodCompileClasspath
|
||||
com.google.appengine:appengine-api-1.0-sdk:2.0.9=default,deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.appengine:appengine-api-stubs:2.0.9=default,deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.appengine:appengine-remote-api:2.0.5=compileClasspath,nonprodCompileClasspath
|
||||
com.google.appengine:appengine-remote-api:2.0.9=default,deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.appengine:appengine-testing:2.0.9=default,deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.appengine:appengine-tools-sdk:2.0.9=default,deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auth:google-auth-library-credentials:1.8.0=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auth:google-auth-library-oauth2-http:1.8.0=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auto.service:auto-service-annotations:1.0.1=annotationProcessor,compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -299,7 +302,7 @@ javax.validation:validation-api:1.0.0.GA=compileClasspath,default,deploy_jar,non
|
||||
javax.xml.bind:jaxb-api:2.3.1=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
javax.xml.bind:jaxb-api:2.4.0-b180830.0359=jaxb
|
||||
jline:jline:1.0=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
joda-time:joda-time:2.10.10=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
joda-time:joda-time:2.10.13=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
junit:junit:4.13.2=default,nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
net.arnx:nashorn-promise:0.1.1=nonprodRuntime,runtime,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy-agent:1.12.10=testCompileClasspath,testRuntimeClasspath
|
||||
|
||||
@@ -44,7 +44,7 @@ import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Hard deletes load-test Contacts, Hosts, their subordinate history entries, and the associated
|
||||
* ForeignKey and EppResourceIndex entities.
|
||||
* ForeignKey entities.
|
||||
*
|
||||
* <p>This only deletes contacts and hosts, NOT domains. To delete domains, use {@link
|
||||
* DeleteProberDataAction} and pass it the TLD(s) that the load test domains were created on. Note
|
||||
|
||||
@@ -53,7 +53,7 @@ import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* Deletes all prober {@link Domain}s and their subordinate history entries, poll messages, and
|
||||
* billing events, along with their ForeignKeyDomainIndex and EppResourceIndex entities.
|
||||
* billing events, along with their ForeignKeyDomainIndex entities.
|
||||
*/
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
@@ -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)))
|
||||
|
||||
@@ -14,11 +14,14 @@
|
||||
|
||||
package google.registry.beam.resave;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static org.apache.beam.sdk.values.TypeDescriptors.integers;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Streams;
|
||||
import google.registry.beam.common.RegistryJpaIO;
|
||||
import google.registry.beam.common.RegistryJpaIO.Read;
|
||||
import google.registry.model.EppResource;
|
||||
@@ -27,7 +30,7 @@ import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import java.io.Serializable;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
@@ -69,7 +72,7 @@ public class ResaveAllEppResourcesPipeline implements Serializable {
|
||||
* multiple times, and to avoid projecting and resaving the same domain multiple times.
|
||||
*/
|
||||
private static final String DOMAINS_TO_PROJECT_QUERY =
|
||||
"FROM Domain d WHERE (d.transferData.transferStatus = 'PENDING' AND"
|
||||
"SELECT repoId FROM Domain d WHERE (d.transferData.transferStatus = 'PENDING' AND"
|
||||
+ " d.transferData.pendingTransferExpirationTime < current_timestamp()) OR"
|
||||
+ " (d.registrationExpirationTime < current_timestamp() AND d.deletionTime ="
|
||||
+ " (:END_OF_TIME)) OR (EXISTS (SELECT 1 FROM GracePeriod gp WHERE gp.domainRepoId ="
|
||||
@@ -88,7 +91,6 @@ public class ResaveAllEppResourcesPipeline implements Serializable {
|
||||
}
|
||||
|
||||
void setupPipeline(Pipeline pipeline) {
|
||||
options.setIsolationOverride(TransactionIsolationLevel.TRANSACTION_READ_COMMITTED);
|
||||
if (options.getFast()) {
|
||||
fastResaveContacts(pipeline);
|
||||
fastResaveDomains(pipeline);
|
||||
@@ -99,13 +101,13 @@ public class ResaveAllEppResourcesPipeline implements Serializable {
|
||||
|
||||
/** Projects to the current time and saves any contacts with expired transfers. */
|
||||
private void fastResaveContacts(Pipeline pipeline) {
|
||||
Read<Contact, Contact> read =
|
||||
Read<String, String> repoIdRead =
|
||||
RegistryJpaIO.read(
|
||||
"FROM Contact WHERE transferData.transferStatus = 'PENDING' AND"
|
||||
"SELECT repoId FROM Contact WHERE transferData.transferStatus = 'PENDING' AND"
|
||||
+ " transferData.pendingTransferExpirationTime < current_timestamp()",
|
||||
Contact.class,
|
||||
c -> c);
|
||||
projectAndResaveResources(pipeline, Contact.class, read);
|
||||
String.class,
|
||||
r -> r);
|
||||
projectAndResaveResources(pipeline, Contact.class, repoIdRead);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,61 +118,72 @@ public class ResaveAllEppResourcesPipeline implements Serializable {
|
||||
* DomainBase#cloneProjectedAtTime(DateTime)}.
|
||||
*/
|
||||
private void fastResaveDomains(Pipeline pipeline) {
|
||||
Read<Domain, Domain> read =
|
||||
Read<String, String> repoIdRead =
|
||||
RegistryJpaIO.read(
|
||||
DOMAINS_TO_PROJECT_QUERY,
|
||||
ImmutableMap.of("END_OF_TIME", DateTimeUtils.END_OF_TIME),
|
||||
Domain.class,
|
||||
d -> d);
|
||||
projectAndResaveResources(pipeline, Domain.class, read);
|
||||
String.class,
|
||||
r -> r);
|
||||
projectAndResaveResources(pipeline, Domain.class, repoIdRead);
|
||||
}
|
||||
|
||||
/** Projects all resources to the current time and saves them. */
|
||||
private <T extends EppResource> void forceResaveAllResources(Pipeline pipeline, Class<T> clazz) {
|
||||
Read<T, T> read = RegistryJpaIO.read(() -> CriteriaQueryBuilder.create(clazz).build());
|
||||
projectAndResaveResources(pipeline, clazz, read);
|
||||
Read<String, String> repoIdRead =
|
||||
RegistryJpaIO.read(
|
||||
// Note: cannot use SQL parameters for the table name
|
||||
String.format("SELECT repoId FROM %s", clazz.getSimpleName()), String.class, r -> r);
|
||||
projectAndResaveResources(pipeline, clazz, repoIdRead);
|
||||
}
|
||||
|
||||
/** Projects and re-saves the result of the provided {@link Read}. */
|
||||
/** Projects and re-saves all resources with repo IDs provided by the {@link Read}. */
|
||||
private <T extends EppResource> void projectAndResaveResources(
|
||||
Pipeline pipeline, Class<T> clazz, Read<?, T> read) {
|
||||
Pipeline pipeline, Class<T> clazz, Read<?, String> repoIdRead) {
|
||||
int numShards = options.getSqlWriteShards();
|
||||
int batchSize = options.getSqlWriteBatchSize();
|
||||
String className = clazz.getSimpleName();
|
||||
pipeline
|
||||
.apply("Read " + className, read)
|
||||
.apply("Read " + className, repoIdRead)
|
||||
.apply(
|
||||
"Shard data for class" + className,
|
||||
WithKeys.<Integer, T>of(e -> ThreadLocalRandom.current().nextInt(numShards))
|
||||
WithKeys.<Integer, String>of(e -> ThreadLocalRandom.current().nextInt(numShards))
|
||||
.withKeyType(integers()))
|
||||
.apply(
|
||||
"Group into batches for class" + className,
|
||||
GroupIntoBatches.<Integer, T>ofSize(batchSize).withShardedKey())
|
||||
.apply("Map " + className + " to now", ParDo.of(new BatchedProjectionFunction<>()))
|
||||
GroupIntoBatches.<Integer, String>ofSize(batchSize).withShardedKey())
|
||||
.apply(
|
||||
"Write transformed " + className,
|
||||
RegistryJpaIO.<EppResource>write()
|
||||
.withName("Write transformed " + className)
|
||||
.withBatchSize(batchSize)
|
||||
.withShards(numShards));
|
||||
"Load, map, and save " + className,
|
||||
ParDo.of(new BatchedLoadProjectAndSaveFunction(clazz)));
|
||||
}
|
||||
|
||||
private static class BatchedProjectionFunction<T extends EppResource>
|
||||
extends DoFn<KV<ShardedKey<Integer>, Iterable<T>>, EppResource> {
|
||||
/** Function that loads, projects, and saves resources all in the same transaction. */
|
||||
private static class BatchedLoadProjectAndSaveFunction
|
||||
extends DoFn<KV<ShardedKey<Integer>, Iterable<String>>, Void> {
|
||||
|
||||
private final Class<? extends EppResource> clazz;
|
||||
|
||||
private BatchedLoadProjectAndSaveFunction(Class<? extends EppResource> clazz) {
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
@ProcessElement
|
||||
public void processElement(
|
||||
@Element KV<ShardedKey<Integer>, Iterable<T>> element,
|
||||
OutputReceiver<EppResource> outputReceiver) {
|
||||
@Element KV<ShardedKey<Integer>, Iterable<String>> element,
|
||||
OutputReceiver<Void> outputReceiver) {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
element
|
||||
.getValue()
|
||||
.forEach(
|
||||
resource ->
|
||||
outputReceiver.output(
|
||||
resource.cloneProjectedAtTime(jpaTm().getTransactionTime()))));
|
||||
() -> {
|
||||
DateTime now = jpaTm().getTransactionTime();
|
||||
ImmutableList<VKey<? extends EppResource>> keys =
|
||||
Streams.stream(element.getValue())
|
||||
.map(repoId -> VKey.create(clazz, repoId))
|
||||
.collect(toImmutableList());
|
||||
ImmutableList<EppResource> mappedResources =
|
||||
jpaTm().loadByKeys(keys).values().stream()
|
||||
.map(r -> r.cloneProjectedAtTime(now))
|
||||
.collect(toImmutableList());
|
||||
jpaTm().putAll(mappedResources);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,6 +193,7 @@ public class ResaveAllEppResourcesPipeline implements Serializable {
|
||||
PipelineOptionsFactory.fromArgs(args)
|
||||
.withValidation()
|
||||
.as(ResaveAllEppResourcesPipelineOptions.class);
|
||||
options.setIsolationOverride(TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ);
|
||||
new ResaveAllEppResourcesPipeline(options).run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,28 +25,34 @@ 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";
|
||||
private static final String OUTDATED_NAME_FIELD = "fullyQualifiedDomainName";
|
||||
|
||||
/** 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}. */
|
||||
public static ThreatMatch fromJSON(JSONObject threatMatch) throws JSONException {
|
||||
// TODO: delete OUTDATED_NAME_FIELD once we no longer process reports saved with
|
||||
// fullyQualifiedDomainName in them, likely 2023
|
||||
return new AutoValue_ThreatMatch(
|
||||
threatMatch.getString(THREAT_TYPE_FIELD), threatMatch.getString(DOMAIN_NAME_FIELD));
|
||||
threatMatch.getString(THREAT_TYPE_FIELD),
|
||||
threatMatch.has(OUTDATED_NAME_FIELD)
|
||||
? threatMatch.getString(OUTDATED_NAME_FIELD)
|
||||
: threatMatch.getString(DOMAIN_NAME_FIELD));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1482,11 +1482,6 @@ public final class RegistryConfig {
|
||||
return CONFIG_SETTINGS.get().registryPolicy.defaultRegistrarWhoisServer;
|
||||
}
|
||||
|
||||
/** Returns the number of {@code EppResourceIndex} buckets to be used. */
|
||||
public static int getEppResourceIndexBucketCount() {
|
||||
return CONFIG_SETTINGS.get().datastore.eppResourceIndexBucketsNum;
|
||||
}
|
||||
|
||||
/** Returns the base retry duration that gets doubled after each failure within {@code Ofy}. */
|
||||
public static Duration getBaseOfyRetryDuration() {
|
||||
return Duration.millis(CONFIG_SETTINGS.get().datastore.baseOfyRetryMillis);
|
||||
|
||||
@@ -108,7 +108,6 @@ public class RegistryConfigSettings {
|
||||
|
||||
/** Configuration for Cloud Datastore. */
|
||||
public static class Datastore {
|
||||
public int eppResourceIndexBucketsNum;
|
||||
public int baseOfyRetryMillis;
|
||||
}
|
||||
|
||||
|
||||
@@ -183,10 +183,6 @@ registryPolicy:
|
||||
requireSslCertificates: true
|
||||
|
||||
datastore:
|
||||
# Number of EPP resource index buckets in Datastore. Don’t change after
|
||||
# initial install.
|
||||
eppResourceIndexBucketsNum: 997
|
||||
|
||||
# Milliseconds that Objectify waits to retry a Datastore transaction (this
|
||||
# doubles after each failure).
|
||||
baseOfyRetryMillis: 100
|
||||
|
||||
@@ -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()),
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>backend</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>default</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>pubapi</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>tools</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>backend</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>default</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>pubapi</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>tools</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>backend</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>default</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>pubapi</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>tools</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>backend</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>default</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>pubapi</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>tools</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>backend</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>default</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>F4_1G</instance-class>
|
||||
<automatic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>pubapi</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>tools</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>backend</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>default</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
|
||||
@@ -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>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>pubapi</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<service>tools</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -23,7 +23,6 @@ import static google.registry.model.IdService.allocateId;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.ExtensionManager;
|
||||
@@ -40,7 +39,6 @@ import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.CreateData.ContactCreateData;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.index.EppResourceIndex;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import javax.inject.Inject;
|
||||
@@ -95,11 +93,7 @@ public final class ContactCreateFlow implements TransactionalFlow {
|
||||
.setType(HistoryEntry.Type.CONTACT_CREATE)
|
||||
.setXmlBytes(null) // We don't want to store contact details in the history entry.
|
||||
.setContact(newContact);
|
||||
tm().insertAll(
|
||||
ImmutableSet.of(
|
||||
newContact,
|
||||
historyBuilder.build(),
|
||||
EppResourceIndex.create(Key.create(newContact))));
|
||||
tm().insertAll(ImmutableSet.of(newContact, historyBuilder.build()));
|
||||
return responseBuilder
|
||||
.setResData(ContactCreateData.create(newContact.getContactId(), now))
|
||||
.build();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -59,7 +59,6 @@ import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.dns.DnsQueue;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.CommandUseErrorException;
|
||||
@@ -105,7 +104,6 @@ import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.CreateData.DomainCreateData;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.index.EppResourceIndex;
|
||||
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.poll.PollMessage.Autorenew;
|
||||
@@ -153,6 +151,7 @@ import org.joda.time.Duration;
|
||||
* @error {@link DomainCreateFlow.MustHaveSignedMarksInCurrentPhaseException}
|
||||
* @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException}
|
||||
* @error {@link DomainCreateFlow.NoTrademarkedRegistrationsBeforeSunriseException}
|
||||
* @error {@link DomainCreateFlow.PackageDomainRegisteredForTooManyYearsException}
|
||||
* @error {@link DomainCreateFlow.SignedMarksOnlyDuringSunriseException}
|
||||
* @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException}
|
||||
* @error {@link DomainFlowTmchUtils.FoundMarkNotYetValidException}
|
||||
@@ -249,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);
|
||||
@@ -391,6 +390,9 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
.build();
|
||||
if (allocationToken.isPresent()
|
||||
&& allocationToken.get().getTokenType().equals(TokenType.PACKAGE)) {
|
||||
if (years > 1) {
|
||||
throw new PackageDomainRegisteredForTooManyYearsException(allocationToken.get().getToken());
|
||||
}
|
||||
domain =
|
||||
domain.asBuilder().setCurrentPackageToken(allocationToken.get().createVKey()).build();
|
||||
}
|
||||
@@ -400,10 +402,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
entitiesToSave.add(
|
||||
createNameCollisionOneTimePollMessage(targetId, domainHistory, registrarId, now));
|
||||
}
|
||||
entitiesToSave.add(
|
||||
domain,
|
||||
domainHistory,
|
||||
EppResourceIndex.create(Key.create(domain)));
|
||||
entitiesToSave.add(domain, domainHistory);
|
||||
if (allocationToken.isPresent()
|
||||
&& TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) {
|
||||
entitiesToSave.add(
|
||||
@@ -640,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)
|
||||
@@ -651,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();
|
||||
}
|
||||
@@ -755,4 +751,14 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
ANCHOR_TENANT_CREATE_VALID_YEARS, invalidYears));
|
||||
}
|
||||
}
|
||||
|
||||
/** Package domain registered for too many years. */
|
||||
static class PackageDomainRegisteredForTooManyYearsException extends CommandUseErrorException {
|
||||
public PackageDomainRegisteredForTooManyYearsException(String token) {
|
||||
super(
|
||||
String.format(
|
||||
"The package token %s cannot be used to register names for longer than 1 year.",
|
||||
token));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -63,6 +63,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;
|
||||
@@ -169,19 +170,21 @@ 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);
|
||||
|
||||
String tld = existingDomain.getTld();
|
||||
Registry registry = Registry.get(tld);
|
||||
// An optional extension from the client specifying what they think the transfer should cost.
|
||||
@@ -293,7 +296,8 @@ 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);
|
||||
if (!isSuperuser) {
|
||||
|
||||
@@ -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())
|
||||
@@ -274,7 +274,7 @@ public final class DomainTransferUtils {
|
||||
* renewal, we must issue a cancellation for the autorenew, so that the losing registrar will not
|
||||
* be charged (essentially, the gaining registrar takes on the cost of the year of registration
|
||||
* that the autorenew just added). But, if the superuser extension is used to request a transfer
|
||||
* without an additional year then the gaining registrar is not charged for the one year renewal
|
||||
* without an additional year then the gaining registrar is not charged for the one-year renewal
|
||||
* and the losing registrar still needs to be charged for the auto-renew.
|
||||
*
|
||||
* <p>For details on the policy justification, see b/19430703#comment17 and <a
|
||||
|
||||
@@ -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,9 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
DomainHistory domainHistory =
|
||||
historyBuilder.setType(DOMAIN_UPDATE).setDomain(newDomain).build();
|
||||
validateNewState(newDomain);
|
||||
dnsQueue.addDomainRefreshTask(targetId);
|
||||
if (requiresDnsUpdate(existingDomain, newDomain)) {
|
||||
dnsQueue.addDomainRefreshTask(targetId);
|
||||
}
|
||||
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||
entitiesToSave.add(newDomain, domainHistory);
|
||||
Optional<BillingEvent.OneTime> statusUpdateBillingEvent =
|
||||
@@ -203,6 +206,16 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
return responseBuilder.build();
|
||||
}
|
||||
|
||||
/** Determines if any of the changes to new domain should trigger DNS update. */
|
||||
private boolean requiresDnsUpdate(Domain existingDomain, Domain newDomain) {
|
||||
if (existingDomain.shouldPublishToDns() != newDomain.shouldPublishToDns()
|
||||
|| !Objects.equals(newDomain.getDsData(), existingDomain.getDsData())
|
||||
|| !Objects.equals(newDomain.getNsHosts(), existingDomain.getNsHosts())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Fail if the object doesn't exist or was deleted. */
|
||||
private void verifyUpdateAllowed(Update command, Domain existingDomain, DateTime now)
|
||||
throws EppException {
|
||||
@@ -229,8 +242,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 +272,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 +280,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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.dns.DnsQueue;
|
||||
import google.registry.flows.EppException;
|
||||
@@ -49,7 +48,6 @@ import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.host.HostCommand.Create;
|
||||
import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.index.EppResourceIndex;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
@@ -106,7 +104,7 @@ public final class HostCreateFlow implements TransactionalFlow {
|
||||
DateTime now = tm().getTransactionTime();
|
||||
verifyResourceDoesNotExist(Host.class, targetId, now, registrarId);
|
||||
// The superordinate domain of the host object if creating an in-bailiwick host, or null if
|
||||
// creating an external host. This is looked up before we actually create the Host object so
|
||||
// creating an external host. This is looked up before we actually create the Host object, so
|
||||
// we can detect error conditions earlier.
|
||||
Optional<Domain> superordinateDomain =
|
||||
lookupSuperordinateDomain(validateHostName(targetId), now);
|
||||
@@ -130,17 +128,13 @@ public final class HostCreateFlow implements TransactionalFlow {
|
||||
.setSuperordinateDomain(superordinateDomain.map(Domain::createVKey).orElse(null))
|
||||
.build();
|
||||
historyBuilder.setType(HOST_CREATE).setHost(newHost);
|
||||
ImmutableSet<ImmutableObject> entitiesToSave =
|
||||
ImmutableSet.of(
|
||||
newHost,
|
||||
historyBuilder.build(),
|
||||
EppResourceIndex.create(Key.create(newHost)));
|
||||
ImmutableSet<ImmutableObject> entitiesToSave = ImmutableSet.of(newHost, historyBuilder.build());
|
||||
if (superordinateDomain.isPresent()) {
|
||||
tm().update(
|
||||
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.
|
||||
@@ -152,14 +146,14 @@ public final class HostCreateFlow implements TransactionalFlow {
|
||||
|
||||
/** Subordinate hosts must have an ip address. */
|
||||
static class SubordinateHostMustHaveIpException extends RequiredParameterMissingException {
|
||||
public SubordinateHostMustHaveIpException() {
|
||||
SubordinateHostMustHaveIpException() {
|
||||
super("Subordinate hosts must have an ip address");
|
||||
}
|
||||
}
|
||||
|
||||
/** External hosts must not have ip addresses. */
|
||||
static class UnexpectedExternalHostIpException extends ParameterValueRangeErrorException {
|
||||
public UnexpectedExternalHostIpException() {
|
||||
UnexpectedExternalHostIpException() {
|
||||
super("External hosts must not have ip addresses");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -23,8 +23,6 @@ import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.index.EppResourceIndex;
|
||||
import google.registry.model.index.EppResourceIndexBucket;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
|
||||
/** Sets of classes of the Objectify-registered entities in use throughout the model. */
|
||||
@@ -38,8 +36,6 @@ public final class EntityClasses {
|
||||
ContactHistory.class,
|
||||
Domain.class,
|
||||
DomainHistory.class,
|
||||
EppResourceIndex.class,
|
||||
EppResourceIndexBucket.class,
|
||||
GaeUserIdConverter.class,
|
||||
HistoryEntry.class,
|
||||
Host.class,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -38,7 +38,7 @@ import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.transfer.TransferData.TransferServerApproveEntity;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithLongVKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import google.registry.persistence.converter.JodaMoneyType;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -295,7 +295,7 @@ public abstract class BillingEvent extends ImmutableObject
|
||||
@Index(columnList = "cancellation_matching_billing_recurrence_id")
|
||||
})
|
||||
@AttributeOverride(name = "id", column = @Column(name = "billing_event_id"))
|
||||
@WithLongVKey(compositeKey = true)
|
||||
@WithVKey(Long.class)
|
||||
public static class OneTime extends BillingEvent {
|
||||
|
||||
/** The billable value. */
|
||||
@@ -473,7 +473,7 @@ public abstract class BillingEvent extends ImmutableObject
|
||||
@Index(columnList = "recurrence_time_of_year")
|
||||
})
|
||||
@AttributeOverride(name = "id", column = @Column(name = "billing_recurrence_id"))
|
||||
@WithLongVKey(compositeKey = true)
|
||||
@WithVKey(Long.class)
|
||||
public static class Recurring extends BillingEvent {
|
||||
|
||||
/**
|
||||
@@ -606,7 +606,7 @@ public abstract class BillingEvent extends ImmutableObject
|
||||
@Index(columnList = "billing_recurrence_id")
|
||||
})
|
||||
@AttributeOverride(name = "id", column = @Column(name = "billing_cancellation_id"))
|
||||
@WithLongVKey(compositeKey = true)
|
||||
@WithVKey(Long.class)
|
||||
public static class Cancellation extends BillingEvent {
|
||||
|
||||
/** The billing time of the charge that is being cancelled. */
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -17,7 +17,7 @@ package google.registry.model.bulkquery;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithStringVKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.Entity;
|
||||
@@ -31,7 +31,7 @@ import javax.persistence.Entity;
|
||||
* <p>Please refer to {@link BulkQueryEntities} for more information.
|
||||
*/
|
||||
@Entity(name = "Domain")
|
||||
@WithStringVKey
|
||||
@WithVKey(String.class)
|
||||
@Access(AccessType.FIELD)
|
||||
public class DomainLite extends DomainBase {
|
||||
|
||||
|
||||
@@ -14,12 +14,11 @@
|
||||
|
||||
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;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithStringVKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.Entity;
|
||||
@@ -46,13 +45,13 @@ import org.joda.time.DateTime;
|
||||
@Index(columnList = "searchName")
|
||||
})
|
||||
@ExternalMessagingName("contact")
|
||||
@WithStringVKey
|
||||
@WithVKey(String.class)
|
||||
@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,10 +19,10 @@ 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;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
@@ -66,7 +66,7 @@ import org.joda.time.DateTime;
|
||||
@Index(columnList = "transfer_billing_event_id"),
|
||||
@Index(columnList = "transfer_billing_recurrence_id")
|
||||
})
|
||||
@WithStringVKey
|
||||
@WithVKey(String.class)
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ import google.registry.model.common.TimedTransitionProperty;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.DomainHistoryVKey;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithStringVKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -58,7 +58,7 @@ import org.joda.time.DateTime;
|
||||
|
||||
/** An entity representing an allocation token. */
|
||||
@Entity
|
||||
@WithStringVKey(compositeKey = true)
|
||||
@WithVKey(String.class)
|
||||
@Table(
|
||||
indexes = {
|
||||
@Index(columnList = "token", name = "allocation_token_token_idx", unique = true),
|
||||
@@ -300,6 +300,9 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
|
||||
!getInstance().tokenType.equals(TokenType.PACKAGE)
|
||||
|| getInstance().renewalPriceBehavior.equals(RenewalPriceBehavior.SPECIFIED),
|
||||
"Package tokens must have renewalPriceBehavior set to SPECIFIED");
|
||||
checkArgument(
|
||||
!getInstance().tokenType.equals(TokenType.PACKAGE) || !getInstance().discountPremiums,
|
||||
"Package tokens cannot discount premium names");
|
||||
checkArgument(
|
||||
getInstance().domainName == null || TokenType.SINGLE_USE.equals(getInstance().tokenType),
|
||||
"Domain name can only be specified for SINGLE_USE tokens");
|
||||
|
||||
@@ -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,13 +14,12 @@
|
||||
|
||||
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;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithStringVKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
|
||||
@@ -52,7 +51,7 @@ import javax.persistence.AccessType;
|
||||
@javax.persistence.Index(columnList = "currentSponsorRegistrarId")
|
||||
})
|
||||
@ExternalMessagingName("host")
|
||||
@WithStringVKey
|
||||
@WithVKey(String.class)
|
||||
@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,79 +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 google.registry.util.TypeUtils.instantiate;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
import com.googlecode.objectify.annotation.Index;
|
||||
import com.googlecode.objectify.annotation.Parent;
|
||||
import google.registry.model.BackupGroupRoot;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.annotations.DeleteAfterMigration;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.persistence.VKey;
|
||||
|
||||
/** An index that allows for quick enumeration of all EppResource entities (e.g. via map reduce). */
|
||||
@ReportedOn
|
||||
@Entity
|
||||
@DeleteAfterMigration
|
||||
public class EppResourceIndex extends BackupGroupRoot {
|
||||
|
||||
@Id String id;
|
||||
|
||||
@Parent Key<EppResourceIndexBucket> bucket;
|
||||
|
||||
/** Although this field holds a {@link Key} it is named "reference" for historical reasons. */
|
||||
Key<? extends EppResource> reference;
|
||||
|
||||
@Index String kind;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
public Key<? extends EppResource> getKey() {
|
||||
return reference;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Key<EppResourceIndexBucket> getBucket() {
|
||||
return bucket;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static <T extends EppResource> EppResourceIndex create(
|
||||
Key<EppResourceIndexBucket> bucket, Key<T> resourceKey) {
|
||||
EppResourceIndex instance = instantiate(EppResourceIndex.class);
|
||||
instance.reference = resourceKey;
|
||||
instance.kind = resourceKey.getKind();
|
||||
// creates a web-safe key string, this value is never used
|
||||
// TODO(b/211785379): remove unused id
|
||||
instance.id = VKey.from(resourceKey).stringify();
|
||||
instance.bucket = bucket;
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static <T extends EppResource> EppResourceIndex create(Key<T> resourceKey) {
|
||||
return create(EppResourceIndexBucket.getBucketKey(resourceKey), resourceKey);
|
||||
}
|
||||
}
|
||||
@@ -1,68 +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 google.registry.config.RegistryConfig.getEppResourceIndexBucketCount;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.annotations.DeleteAfterMigration;
|
||||
import google.registry.model.annotations.VirtualEntity;
|
||||
|
||||
/** A virtual entity to represent buckets to which EppResourceIndex objects are randomly added. */
|
||||
@Entity
|
||||
@VirtualEntity
|
||||
@DeleteAfterMigration
|
||||
public class EppResourceIndexBucket extends ImmutableObject {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Id
|
||||
private long bucketId;
|
||||
|
||||
/**
|
||||
* Deterministic function that returns a bucket id based on the resource's roid.
|
||||
* NB: At the moment, nothing depends on this being deterministic, so we have the ability to
|
||||
* change the number of buckets and utilize a random distribution once we do.
|
||||
*/
|
||||
private static long getBucketIdFromEppResource(Key<? extends EppResource> resourceKey) {
|
||||
int numBuckets = getEppResourceIndexBucketCount();
|
||||
// IDs can't be 0, so add 1 to the hash.
|
||||
return Hashing.consistentHash(resourceKey.getName().hashCode(), numBuckets) + 1L;
|
||||
}
|
||||
|
||||
/** Gets a bucket key as a function of an EppResource to be indexed. */
|
||||
public static Key<EppResourceIndexBucket> getBucketKey(Key<? extends EppResource> resourceKey) {
|
||||
return Key.create(EppResourceIndexBucket.class, getBucketIdFromEppResource(resourceKey));
|
||||
}
|
||||
|
||||
/** Gets the specified numbered bucket key. */
|
||||
public static Key<EppResourceIndexBucket> getBucketKey(int bucketId) {
|
||||
return Key.create(EppResourceIndexBucket.class, bucketId);
|
||||
}
|
||||
|
||||
/** Returns the keys to all buckets. */
|
||||
public static Iterable<Key<EppResourceIndexBucket>> getAllBuckets() {
|
||||
ImmutableList.Builder<Key<EppResourceIndexBucket>> builder = new ImmutableList.Builder<>();
|
||||
for (int bucketId = 1; bucketId <= getEppResourceIndexBucketCount(); bucketId++) {
|
||||
builder.add(getBucketKey(bucketId));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ import google.registry.model.transfer.TransferResponse;
|
||||
import google.registry.model.transfer.TransferResponse.ContactTransferResponse;
|
||||
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithLongVKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import google.registry.util.NullIgnoringCollectionBuilder;
|
||||
import java.util.Optional;
|
||||
import javax.persistence.AttributeOverride;
|
||||
@@ -342,7 +342,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||
*/
|
||||
@Entity
|
||||
@DiscriminatorValue("ONE_TIME")
|
||||
@WithLongVKey(compositeKey = true)
|
||||
@WithVKey(Long.class)
|
||||
public static class OneTime extends PollMessage {
|
||||
|
||||
@Embedded
|
||||
@@ -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();
|
||||
}
|
||||
@@ -545,7 +544,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||
*/
|
||||
@Entity
|
||||
@DiscriminatorValue("AUTORENEW")
|
||||
@WithLongVKey(compositeKey = true)
|
||||
@WithVKey(Long.class)
|
||||
public static class Autorenew extends PollMessage {
|
||||
|
||||
/** The target id of the autorenew event. */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import javax.persistence.Embeddable;
|
||||
|
||||
/** Transfer data for contact. */
|
||||
@Embeddable
|
||||
public class ContactTransferData extends TransferData<ContactTransferData.Builder> {
|
||||
public class ContactTransferData extends TransferData {
|
||||
public static final ContactTransferData EMPTY = new ContactTransferData();
|
||||
|
||||
@Override
|
||||
@@ -26,6 +26,11 @@ public class ContactTransferData extends TransferData<ContactTransferData.Builde
|
||||
return EMPTY.equals(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Builder createEmptyBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
|
||||
@@ -34,7 +34,7 @@ import org.joda.time.DateTime;
|
||||
|
||||
/** Transfer data for domain. */
|
||||
@Embeddable
|
||||
public class DomainTransferData extends TransferData<DomainTransferData.Builder> {
|
||||
public class DomainTransferData extends TransferData {
|
||||
public static final DomainTransferData EMPTY = new DomainTransferData();
|
||||
|
||||
/**
|
||||
@@ -107,7 +107,7 @@ public class DomainTransferData extends TransferData<DomainTransferData.Builder>
|
||||
|
||||
@Override
|
||||
public Builder copyConstantFieldsToBuilder() {
|
||||
return super.copyConstantFieldsToBuilder().setTransferPeriod(transferPeriod);
|
||||
return ((Builder) super.copyConstantFieldsToBuilder()).setTransferPeriod(transferPeriod);
|
||||
}
|
||||
|
||||
public Period getTransferPeriod() {
|
||||
@@ -157,6 +157,11 @@ public class DomainTransferData extends TransferData<DomainTransferData.Builder>
|
||||
return EMPTY.equals(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Builder createEmptyBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
|
||||
@@ -27,7 +27,6 @@ import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.NullIgnoringCollectionBuilder;
|
||||
import google.registry.util.TypeUtils.TypeInstantiator;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.AttributeOverride;
|
||||
@@ -41,10 +40,7 @@ import javax.persistence.MappedSuperclass;
|
||||
* are implicitly transferred with their superordinate domain.
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public abstract class TransferData<
|
||||
B extends
|
||||
TransferData.Builder<? extends TransferData<?>, ? extends TransferData.Builder<?, ?>>>
|
||||
extends BaseTransferObject implements Buildable {
|
||||
public abstract class TransferData extends BaseTransferObject implements Buildable {
|
||||
|
||||
/** The transaction id of the most recent transfer request (or null if there never was one). */
|
||||
@Embedded
|
||||
@@ -102,6 +98,8 @@ public abstract class TransferData<
|
||||
@Override
|
||||
public abstract Builder<?, ?> asBuilder();
|
||||
|
||||
protected abstract Builder<?, ?> createEmptyBuilder();
|
||||
|
||||
/**
|
||||
* Returns a fresh Builder populated only with the constant fields of this TransferData, i.e.
|
||||
* those that are fixed and unchanging throughout the transfer process.
|
||||
@@ -116,8 +114,8 @@ public abstract class TransferData<
|
||||
* <li>transferPeriod
|
||||
* </ul>
|
||||
*/
|
||||
public B copyConstantFieldsToBuilder() {
|
||||
B newBuilder = new TypeInstantiator<B>(getClass()) {}.instantiate();
|
||||
public Builder<?, ?> copyConstantFieldsToBuilder() {
|
||||
Builder<?, ?> newBuilder = createEmptyBuilder();
|
||||
newBuilder
|
||||
.setTransferRequestTrid(transferRequestTrid)
|
||||
.setTransferRequestTime(transferRequestTime)
|
||||
@@ -129,7 +127,7 @@ public abstract class TransferData<
|
||||
/** Maps serverApproveEntities set to the individual fields. */
|
||||
static void mapServerApproveEntitiesToFields(
|
||||
Set<VKey<? extends TransferServerApproveEntity>> serverApproveEntities,
|
||||
TransferData<?> transferData) {
|
||||
TransferData transferData) {
|
||||
if (isNullOrEmpty(serverApproveEntities)) {
|
||||
transferData.pollMessageId1 = null;
|
||||
transferData.pollMessageId2 = null;
|
||||
@@ -161,7 +159,7 @@ public abstract class TransferData<
|
||||
}
|
||||
|
||||
/** Builder for {@link TransferData} because it is immutable. */
|
||||
public abstract static class Builder<T extends TransferData<?>, B extends Builder<T, B>>
|
||||
public abstract static class Builder<T extends TransferData, B extends Builder<T, B>>
|
||||
extends BaseTransferObject.Builder<T, B> {
|
||||
|
||||
/** Create a {@link Builder} wrapping a new 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ public abstract class PersistenceModule {
|
||||
replicaInstanceConnectionName.ifPresent(
|
||||
name -> overrides.put(HIKARI_DS_CLOUD_SQL_INSTANCE, name));
|
||||
overrides.put(
|
||||
Environment.ISOLATION, TransactionIsolationLevel.TRANSACTION_READ_COMMITTED.name());
|
||||
Environment.ISOLATION, TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ.name());
|
||||
return new JpaTransactionManagerImpl(create(overrides), clock);
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ public abstract class PersistenceModule {
|
||||
replicaInstanceConnectionName.ifPresent(
|
||||
name -> overrides.put(HIKARI_DS_CLOUD_SQL_INSTANCE, name));
|
||||
overrides.put(
|
||||
Environment.ISOLATION, TransactionIsolationLevel.TRANSACTION_READ_COMMITTED.name());
|
||||
Environment.ISOLATION, TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ.name());
|
||||
return new JpaTransactionManagerImpl(create(overrides), clock);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright 2020 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.persistence;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Entity;
|
||||
|
||||
/**
|
||||
* Annotation for {@link Entity} which id is string type and needs an {@link AttributeConverter} for
|
||||
* its VKey.
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface WithStringVKey {
|
||||
/**
|
||||
* Sets the suffix of the class name for the {@link AttributeConverter} generated by
|
||||
* StringVKeyProcessor. If not set, the suffix will be the type name of the VKey. Note that the
|
||||
* class name will be "VKeyConverter_" concatenated with the suffix.
|
||||
*/
|
||||
String classNameSuffix() default "";
|
||||
|
||||
/**
|
||||
* Set to true if this is a composite vkey.
|
||||
*
|
||||
* <p>For composite VKeys, we don't attempt to define an objectify key when loading from SQL: the
|
||||
* enclosing class has to take care of that.
|
||||
*/
|
||||
boolean compositeKey() default false;
|
||||
}
|
||||
+11
-18
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
// 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.
|
||||
@@ -14,29 +14,22 @@
|
||||
|
||||
package google.registry.persistence;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Entity;
|
||||
|
||||
/**
|
||||
* Annotation for {@link Entity} which id is long type and needs an {@link AttributeConverter} for
|
||||
* its VKey.
|
||||
* Annotation for {@link Entity} that can be saved as a foreign key in the form of a {@link VKey} in
|
||||
* another table.
|
||||
*
|
||||
* <p>A {@link AttributeConverter} named {@code VKeyConverter_[EntityClassSimpleName]} will be
|
||||
* automatically generated by {@code google.registry.processors.VKeyProcessor}, this class must be
|
||||
* manually added to {@code persistence.xml} in order for it to be picked up by Hibernate.
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface WithLongVKey {
|
||||
/**
|
||||
* Sets the suffix of the class name for the {@link AttributeConverter} generated by
|
||||
* LongVKeyProcessor. If not set, the suffix will be the type name of the VKey. Note that the
|
||||
* class name will be "VKeyConverter_" concatenated with the suffix.
|
||||
*/
|
||||
String classNameSuffix() default "";
|
||||
|
||||
/**
|
||||
* Set to true if this is a composite vkey.
|
||||
*
|
||||
* <p>For composite VKeys, we don't attempt to define an objectify key when loading from SQL: the
|
||||
* enclosing class has to take care of that.
|
||||
*/
|
||||
boolean compositeKey() default false;
|
||||
public @interface WithVKey {
|
||||
/** The type of the SQL primary ID of the entity that is saved in the {@link VKey} */
|
||||
Class<? extends Serializable> value();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user