mirror of
https://github.com/google/nomulus
synced 2026-05-17 13:21:48 +00:00
Compare commits
8 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef9f3aeada | ||
|
|
9c43aab8cd | ||
|
|
cb63c3dd80 | ||
|
|
2cf190e448 | ||
|
|
e550c94cbc | ||
|
|
6e2bbd1a7e | ||
|
|
495d7176d8 | ||
|
|
d7aab524e5 |
@@ -65,7 +65,7 @@ class PresubmitCheck:
|
||||
for pattern in self.skipped_patterns:
|
||||
if pattern in file:
|
||||
return False
|
||||
with open(file, "r") as f:
|
||||
with open(file, "r", encoding='utf8') as f:
|
||||
file_content = f.read()
|
||||
matches = re.match(self.regex, file_content, re.DOTALL)
|
||||
if self.regex_type == FORBIDDEN:
|
||||
@@ -241,7 +241,7 @@ def verify_flyway_index():
|
||||
|
||||
# Remove the sequence numbers and compare against the index file contents.
|
||||
files = [filename[1] for filename in sorted(files)]
|
||||
with open('db/src/main/resources/sql/flyway.txt') as index:
|
||||
with open('db/src/main/resources/sql/flyway.txt', encoding='utf8') as index:
|
||||
indexed_files = index.read().splitlines()
|
||||
if files != indexed_files:
|
||||
unindexed = set(files) - set(indexed_files)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.batch;
|
||||
|
||||
import static google.registry.mapreduce.MapreduceRunner.PARAM_FAST;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
@@ -24,6 +25,7 @@ import google.registry.mapreduce.MapreduceRunner;
|
||||
import google.registry.mapreduce.inputs.EppResourceInputs;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import javax.inject.Inject;
|
||||
@@ -39,6 +41,14 @@ import javax.inject.Inject;
|
||||
* <p>Because there are no auth settings in the {@link Action} annotation, this command can only be
|
||||
* run internally, or by pretending to be internal by setting the X-AppEngine-QueueName header,
|
||||
* which only admin users can do.
|
||||
*
|
||||
* <p>If the <code>?fast=true</code> querystring parameter is passed, then entities that are not
|
||||
* changed by {@link EppResource#cloneProjectedAtTime} will not be re-saved. This helps prevent
|
||||
* mutation load on the DB and has the beneficial side effect of writing out smaller commit logs.
|
||||
* Note that this does NOT pick up mutations caused by migrations using the {@link
|
||||
* com.googlecode.objectify.annotation.OnLoad} annotation, so if you are running a one-off schema
|
||||
* migration, do not use fast mode. Fast mode defaults to false for this reason, but is used by the
|
||||
* monthly invocation of the mapreduce.
|
||||
*/
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
@@ -48,7 +58,13 @@ public class ResaveAllEppResourcesAction implements Runnable {
|
||||
|
||||
@Inject MapreduceRunner mrRunner;
|
||||
@Inject Response response;
|
||||
@Inject ResaveAllEppResourcesAction() {}
|
||||
|
||||
@Inject
|
||||
@Parameter(PARAM_FAST)
|
||||
boolean isFast;
|
||||
|
||||
@Inject
|
||||
ResaveAllEppResourcesAction() {}
|
||||
|
||||
/**
|
||||
* The number of shards to run the map-only mapreduce on.
|
||||
@@ -66,7 +82,7 @@ public class ResaveAllEppResourcesAction implements Runnable {
|
||||
.setModuleName("backend")
|
||||
.setDefaultMapShards(NUM_SHARDS)
|
||||
.runMapOnly(
|
||||
new ResaveAllEppResourcesActionMapper(),
|
||||
new ResaveAllEppResourcesActionMapper(isFast),
|
||||
ImmutableList.of(EppResourceInputs.createKeyInput(EppResource.class)))
|
||||
.sendLinkToMapreduceConsole(response);
|
||||
}
|
||||
@@ -76,23 +92,33 @@ public class ResaveAllEppResourcesAction implements Runnable {
|
||||
extends Mapper<Key<EppResource>, Void, Void> {
|
||||
|
||||
private static final long serialVersionUID = -7721628665138087001L;
|
||||
public ResaveAllEppResourcesActionMapper() {}
|
||||
|
||||
private final boolean isFast;
|
||||
|
||||
ResaveAllEppResourcesActionMapper(boolean isFast) {
|
||||
this.isFast = isFast;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void map(final Key<EppResource> resourceKey) {
|
||||
tm()
|
||||
.transact(
|
||||
() -> {
|
||||
EppResource projectedResource =
|
||||
ofy()
|
||||
.load()
|
||||
.key(resourceKey)
|
||||
.now()
|
||||
.cloneProjectedAtTime(tm().getTransactionTime());
|
||||
ofy().save().entity(projectedResource).now();
|
||||
});
|
||||
getContext().incrementCounter(String.format("%s entities re-saved", resourceKey.getKind()));
|
||||
boolean resaved =
|
||||
tm().transact(
|
||||
() -> {
|
||||
EppResource originalResource = ofy().load().key(resourceKey).now();
|
||||
EppResource projectedResource =
|
||||
originalResource.cloneProjectedAtTime(tm().getTransactionTime());
|
||||
if (isFast && originalResource.equals(projectedResource)) {
|
||||
return false;
|
||||
} else {
|
||||
ofy().save().entity(projectedResource).now();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
getContext()
|
||||
.incrementCounter(
|
||||
String.format(
|
||||
"%s entities %s",
|
||||
resourceKey.getKind(), resaved ? "re-saved" : "with no changes skipped"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import google.registry.persistence.PersistenceModule;
|
||||
import google.registry.persistence.PersistenceModule.JdbcJpaTm;
|
||||
import google.registry.persistence.PersistenceModule.SocketFactoryJpaTm;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import google.registry.privileges.secretmanager.SecretManagerModule;
|
||||
import google.registry.util.UtilsModule;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
@@ -168,6 +169,7 @@ public class BeamJpaModule {
|
||||
BeamJpaModule.class,
|
||||
KmsModule.class,
|
||||
PersistenceModule.class,
|
||||
SecretManagerModule.class,
|
||||
UtilsModule.class
|
||||
})
|
||||
public interface JpaTransactionManagerComponent {
|
||||
|
||||
@@ -43,7 +43,6 @@ import google.registry.backup.CommitLogImports;
|
||||
import google.registry.backup.VersionedEntity;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.ofy.ObjectifyService;
|
||||
import google.registry.model.ofy.Ofy;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import google.registry.tools.LevelDbLogReader;
|
||||
import google.registry.util.SystemSleeper;
|
||||
@@ -432,7 +431,6 @@ public final class Transforms {
|
||||
private final SerializableSupplier<JpaTransactionManager> jpaSupplier;
|
||||
private final SerializableFunction<T, Object> jpaConverter;
|
||||
|
||||
private transient Ofy ofy;
|
||||
private transient SystemSleeper sleeper;
|
||||
|
||||
SqlBatchWriter(
|
||||
@@ -448,7 +446,6 @@ public final class Transforms {
|
||||
|
||||
try (AppEngineEnvironment env = new AppEngineEnvironment()) {
|
||||
ObjectifyService.initOfy();
|
||||
ofy = ObjectifyService.ofy();
|
||||
}
|
||||
|
||||
synchronized (SqlBatchWriter.class) {
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResources]]></url>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResources?fast=true]]></url>
|
||||
<description>
|
||||
This job resaves all our resources, projected in time to "now".
|
||||
It is needed for "deleteOldCommitLogs" to work correctly.
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResources]]></url>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResources?fast=true]]></url>
|
||||
<description>
|
||||
This job resaves all our resources, projected in time to "now".
|
||||
It is needed for "deleteOldCommitLogs" to work correctly.
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResources]]></url>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResources?fast=true]]></url>
|
||||
<description>
|
||||
This job resaves all our resources, projected in time to "now".
|
||||
It is needed for "deleteOldCommitLogs" to work correctly.
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResources]]></url>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResources?fast=true]]></url>
|
||||
<description>
|
||||
This job resaves all our resources, projected in time to "now".
|
||||
It is needed for "deleteOldCommitLogs" to work correctly.
|
||||
|
||||
@@ -110,7 +110,6 @@ import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.persistence.DomainHistoryVKey;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.tmch.LordnTaskUtils;
|
||||
import java.util.Optional;
|
||||
@@ -372,7 +371,7 @@ public class DomainCreateFlow implements TransactionalFlow {
|
||||
&& TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) {
|
||||
entitiesToSave.add(
|
||||
allocationTokenFlowUtils.redeemToken(
|
||||
allocationToken.get(), DomainHistoryVKey.create(Key.create(historyEntry))));
|
||||
allocationToken.get(), HistoryEntry.createVKey(Key.create(historyEntry))));
|
||||
}
|
||||
enqueueTasks(newDomain, hasSignedMarks, hasClaimsNotice);
|
||||
|
||||
|
||||
@@ -15,14 +15,13 @@
|
||||
package google.registry.flows.domain.token;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.AssociationProhibitsOperationException;
|
||||
import google.registry.flows.EppException.AuthorizationErrorException;
|
||||
@@ -32,7 +31,8 @@ import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.persistence.DomainHistoryVKey;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
@@ -107,7 +107,7 @@ public class AllocationTokenFlowUtils {
|
||||
|
||||
/** Redeems a SINGLE_USE {@link AllocationToken}, returning the redeemed copy. */
|
||||
public AllocationToken redeemToken(
|
||||
AllocationToken token, DomainHistoryVKey redemptionHistoryEntry) {
|
||||
AllocationToken token, VKey<? extends HistoryEntry> redemptionHistoryEntry) {
|
||||
checkArgument(
|
||||
TokenType.SINGLE_USE.equals(token.getTokenType()),
|
||||
"Only SINGLE_USE tokens can be marked as redeemed");
|
||||
@@ -152,14 +152,15 @@ public class AllocationTokenFlowUtils {
|
||||
// See https://tools.ietf.org/html/draft-ietf-regext-allocation-token-04#section-2.1
|
||||
throw new InvalidAllocationTokenException();
|
||||
}
|
||||
AllocationToken tokenEntity = ofy().load().key(Key.create(AllocationToken.class, token)).now();
|
||||
if (tokenEntity == null) {
|
||||
Optional<AllocationToken> maybeTokenEntity =
|
||||
tm().maybeLoad(VKey.create(AllocationToken.class, token));
|
||||
if (maybeTokenEntity.isEmpty()) {
|
||||
throw new InvalidAllocationTokenException();
|
||||
}
|
||||
if (tokenEntity.isRedeemed()) {
|
||||
if (maybeTokenEntity.get().isRedeemed()) {
|
||||
throw new AlreadyRedeemedAllocationTokenException();
|
||||
}
|
||||
return tokenEntity;
|
||||
return maybeTokenEntity.get();
|
||||
}
|
||||
|
||||
// Note: exception messages should be <= 32 characters long for domain check results
|
||||
|
||||
@@ -21,10 +21,8 @@ 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.EppResourceUtils.createRepoId;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
import static google.registry.util.CollectionUtils.union;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.googlecode.objectify.Key;
|
||||
@@ -137,13 +135,11 @@ public final class HostCreateFlow implements TransactionalFlow {
|
||||
ImmutableSet<ImmutableObject> entitiesToSave =
|
||||
ImmutableSet.of(
|
||||
newHost,
|
||||
historyBuilder.build(),
|
||||
historyBuilder.build().toChildHistoryEntity(),
|
||||
ForeignKeyIndex.create(newHost, newHost.getDeletionTime()),
|
||||
EppResourceIndex.create(Key.create(newHost)));
|
||||
if (superordinateDomain.isPresent()) {
|
||||
entitiesToSave =
|
||||
union(
|
||||
entitiesToSave,
|
||||
tm().update(
|
||||
superordinateDomain
|
||||
.get()
|
||||
.asBuilder()
|
||||
@@ -153,7 +149,7 @@ public final class HostCreateFlow implements TransactionalFlow {
|
||||
// they are only written as NS records from the referencing domain.
|
||||
dnsQueue.addHostRefreshTask(targetId);
|
||||
}
|
||||
ofy().save().entities(entitiesToSave);
|
||||
tm().insertAll(entitiesToSave);
|
||||
return responseBuilder.setResData(HostCreateData.create(targetId, now)).build();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.mapreduce;
|
||||
|
||||
import static google.registry.mapreduce.MapreduceRunner.PARAM_DRY_RUN;
|
||||
import static google.registry.mapreduce.MapreduceRunner.PARAM_FAST;
|
||||
import static google.registry.mapreduce.MapreduceRunner.PARAM_MAP_SHARDS;
|
||||
import static google.registry.mapreduce.MapreduceRunner.PARAM_REDUCE_SHARDS;
|
||||
import static google.registry.request.RequestParameters.extractBooleanParameter;
|
||||
@@ -36,6 +37,12 @@ public final class MapreduceModule {
|
||||
return extractBooleanParameter(req, PARAM_DRY_RUN);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(PARAM_FAST)
|
||||
static boolean provideIsFast(HttpServletRequest req) {
|
||||
return extractBooleanParameter(req, PARAM_FAST);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(PARAM_MAP_SHARDS)
|
||||
static Optional<Integer> provideMapShards(HttpServletRequest req) {
|
||||
|
||||
@@ -55,6 +55,7 @@ public class MapreduceRunner {
|
||||
public static final String PARAM_DRY_RUN = "dryRun";
|
||||
public static final String PARAM_MAP_SHARDS = "mapShards";
|
||||
public static final String PARAM_REDUCE_SHARDS = "reduceShards";
|
||||
public static final String PARAM_FAST = "fast";
|
||||
|
||||
private static final String BASE_URL = "/_dr/mapreduce/";
|
||||
private static final String QUEUE_NAME = "mapreduce";
|
||||
|
||||
@@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
import static google.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
import static google.registry.util.DateTimeUtils.latestOf;
|
||||
@@ -135,7 +136,7 @@ public final class EppResourceUtils {
|
||||
useCache
|
||||
? ForeignKeyIndex.loadCached(clazz, ImmutableList.of(foreignKey), now)
|
||||
.getOrDefault(foreignKey, null)
|
||||
: ofy().load().type(ForeignKeyIndex.mapToFkiClass(clazz)).id(foreignKey).now();
|
||||
: 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) {
|
||||
return Optional.empty();
|
||||
@@ -143,7 +144,7 @@ public final class EppResourceUtils {
|
||||
T resource =
|
||||
useCache
|
||||
? EppResource.loadCached(fki.getResourceKey())
|
||||
: tm().maybeLoad(fki.getResourceKey()).orElse(null);
|
||||
: transactIfJpaTm(() -> tm().maybeLoad(fki.getResourceKey()).orElse(null));
|
||||
if (resource == null || isAtOrAfter(now, resource.getDeletionTime())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* A timestamp that auto-updates on each save to Datastore.
|
||||
* A timestamp that auto-updates on each save to Datastore/Cloud SQL.
|
||||
*
|
||||
* @see UpdateAutoTimestampTranslatorFactory
|
||||
*/
|
||||
@@ -31,7 +31,7 @@ public class UpdateAutoTimestamp extends ImmutableObject {
|
||||
// When set to true, database converters/translators should do tha auto update. When set to
|
||||
// false, auto update should be suspended (this exists to allow us to preserve the original value
|
||||
// during a replay).
|
||||
static ThreadLocal<Boolean> autoUpdateEnabled = ThreadLocal.withInitial(() -> true);
|
||||
private static ThreadLocal<Boolean> autoUpdateEnabled = ThreadLocal.withInitial(() -> true);
|
||||
|
||||
DateTime timestamp;
|
||||
|
||||
|
||||
@@ -75,7 +75,9 @@ public class DomainBase extends DomainContent
|
||||
}
|
||||
|
||||
@ElementCollection
|
||||
@JoinTable(name = "DomainHost")
|
||||
@JoinTable(
|
||||
name = "DomainHost",
|
||||
indexes = {@Index(columnList = "domain_repo_id,host_repo_id", unique = true)})
|
||||
@Access(AccessType.PROPERTY)
|
||||
@Column(name = "host_repo_id")
|
||||
public Set<VKey<HostResource>> getNsHosts() {
|
||||
|
||||
@@ -96,7 +96,14 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
|
||||
// TODO(b/166776754): Investigate if we can reuse domainContent.nsHosts for storing host keys.
|
||||
@Ignore
|
||||
@ElementCollection
|
||||
@JoinTable(name = "DomainHistoryHost")
|
||||
@JoinTable(
|
||||
name = "DomainHistoryHost",
|
||||
indexes = {
|
||||
@Index(
|
||||
columnList =
|
||||
"domain_history_history_revision_id,domain_history_domain_repo_id,host_repo_id",
|
||||
unique = true),
|
||||
})
|
||||
@Column(name = "host_repo_id")
|
||||
Set<VKey<HostResource>> nsHosts;
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.ofy.ObjectifyService;
|
||||
import google.registry.persistence.BillingVKey.BillingEventVKey;
|
||||
import google.registry.persistence.BillingVKey.BillingRecurrenceVKey;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.schema.replay.DatastoreAndSqlEntity;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -82,10 +84,8 @@ public class GracePeriod extends GracePeriodBase implements DatastoreAndSqlEntit
|
||||
instance.domainRepoId = checkArgumentNotNull(domainRepoId);
|
||||
instance.expirationTime = checkArgumentNotNull(expirationTime);
|
||||
instance.clientId = checkArgumentNotNull(clientId);
|
||||
instance.billingEventOneTime = billingEventOneTime;
|
||||
instance.billingEventOneTimeHistoryId = DomainBase.getHistoryId(billingEventOneTime);
|
||||
instance.billingEventRecurring = billingEventRecurring;
|
||||
instance.billingEventRecurringHistoryId = DomainBase.getHistoryId(billingEventRecurring);
|
||||
instance.billingEventOneTime = BillingEventVKey.create(billingEventOneTime);
|
||||
instance.billingEventRecurring = BillingRecurrenceVKey.create(billingEventRecurring);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -178,7 +178,6 @@ public class GracePeriod extends GracePeriodBase implements DatastoreAndSqlEntit
|
||||
public GracePeriod cloneAfterOfyLoad(String domainRepoId) {
|
||||
GracePeriod clone = clone(this);
|
||||
clone.domainRepoId = checkArgumentNotNull(domainRepoId);
|
||||
clone.restoreHistoryIds();
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -190,7 +189,7 @@ public class GracePeriod extends GracePeriodBase implements DatastoreAndSqlEntit
|
||||
*/
|
||||
public GracePeriod cloneWithRecurringBillingEvent(VKey<BillingEvent.Recurring> recurring) {
|
||||
GracePeriod clone = clone(this);
|
||||
clone.billingEventRecurring = recurring;
|
||||
clone.billingEventRecurring = BillingRecurrenceVKey.create(recurring);
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -232,9 +231,7 @@ public class GracePeriod extends GracePeriodBase implements DatastoreAndSqlEntit
|
||||
instance.expirationTime = gracePeriod.expirationTime;
|
||||
instance.clientId = gracePeriod.clientId;
|
||||
instance.billingEventOneTime = gracePeriod.billingEventOneTime;
|
||||
instance.billingEventOneTimeHistoryId = gracePeriod.billingEventOneTimeHistoryId;
|
||||
instance.billingEventRecurring = gracePeriod.billingEventRecurring;
|
||||
instance.billingEventRecurringHistoryId = gracePeriod.billingEventRecurringHistoryId;
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,14 @@
|
||||
|
||||
package google.registry.model.domain;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Embed;
|
||||
import com.googlecode.objectify.annotation.Ignore;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.ModelUtils;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.OneTime;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.persistence.BillingVKey.BillingEventVKey;
|
||||
import google.registry.persistence.BillingVKey.BillingRecurrenceVKey;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -68,24 +68,16 @@ public class GracePeriodBase extends ImmutableObject {
|
||||
* billingEventRecurring}) or for redemption grace periods (since deletes have no cost).
|
||||
*/
|
||||
// NB: Would @IgnoreSave(IfNull.class), but not allowed for @Embed collections.
|
||||
@Column(name = "billing_event_id")
|
||||
VKey<OneTime> billingEventOneTime = null;
|
||||
|
||||
@Ignore
|
||||
@Column(name = "billing_event_history_id")
|
||||
Long billingEventOneTimeHistoryId;
|
||||
@Access(AccessType.FIELD)
|
||||
BillingEventVKey billingEventOneTime = null;
|
||||
|
||||
/**
|
||||
* The recurring billing event corresponding to the action that triggered this grace period, if
|
||||
* applicable - i.e. if the action was an autorenew - or null in all other cases.
|
||||
*/
|
||||
// NB: Would @IgnoreSave(IfNull.class), but not allowed for @Embed collections.
|
||||
@Column(name = "billing_recurrence_id")
|
||||
VKey<BillingEvent.Recurring> billingEventRecurring = null;
|
||||
|
||||
@Ignore
|
||||
@Column(name = "billing_recurrence_history_id")
|
||||
Long billingEventRecurringHistoryId;
|
||||
@Access(AccessType.FIELD)
|
||||
BillingRecurrenceVKey billingEventRecurring = null;
|
||||
|
||||
public long getGracePeriodId() {
|
||||
return gracePeriodId;
|
||||
@@ -123,8 +115,7 @@ public class GracePeriodBase extends ImmutableObject {
|
||||
* period is not AUTO_RENEW.
|
||||
*/
|
||||
public VKey<BillingEvent.OneTime> getOneTimeBillingEvent() {
|
||||
restoreOfyKeys();
|
||||
return billingEventOneTime;
|
||||
return billingEventOneTime == null ? null : billingEventOneTime.createVKey();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,18 +123,7 @@ public class GracePeriodBase extends ImmutableObject {
|
||||
* period is AUTO_RENEW.
|
||||
*/
|
||||
public VKey<BillingEvent.Recurring> getRecurringBillingEvent() {
|
||||
restoreOfyKeys();
|
||||
return billingEventRecurring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores history ids for composite VKeys after a load from datastore.
|
||||
*
|
||||
* <p>For use by DomainContent.load() ONLY.
|
||||
*/
|
||||
protected void restoreHistoryIds() {
|
||||
billingEventOneTimeHistoryId = DomainBase.getHistoryId(billingEventOneTime);
|
||||
billingEventRecurringHistoryId = DomainBase.getHistoryId(billingEventRecurring);
|
||||
return billingEventRecurring == null ? null : billingEventRecurring.createVKey();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,7 +132,6 @@ public class GracePeriodBase extends ImmutableObject {
|
||||
*/
|
||||
@Override
|
||||
protected Map<Field, Object> getSignificantFields() {
|
||||
restoreOfyKeys();
|
||||
// Can't use streams or ImmutableMap because we can have null values.
|
||||
Map<Field, Object> result = new LinkedHashMap();
|
||||
for (Map.Entry<Field, Object> entry : ModelUtils.getFieldValues(this).entrySet()) {
|
||||
@@ -162,33 +141,4 @@ public class GracePeriodBase extends ImmutableObject {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores Ofy keys in the billing events.
|
||||
*
|
||||
* <p>This must be called by all methods that access the one time or recurring billing event keys.
|
||||
* When the billing event keys are loaded from SQL, they are loaded as asymmetric keys because the
|
||||
* database columns that we load them from do not contain all of the information necessary to
|
||||
* reconsitute the Ofy side of the key. In other cases, we restore the Ofy key during the
|
||||
* hibernate {@link javax.persistence.PostLoad} method from the other fields of the object, but we
|
||||
* have been unable to make this work with hibernate's internal persistence model in this case
|
||||
* because the {@link GracePeriod}'s hash code is evaluated prior to these calls, and would be
|
||||
* invalidated by changing the fields.
|
||||
*/
|
||||
private final synchronized void restoreOfyKeys() {
|
||||
if (billingEventOneTime != null && !billingEventOneTime.maybeGetOfyKey().isPresent()) {
|
||||
billingEventOneTime =
|
||||
DomainBase.restoreOfyFrom(
|
||||
Key.create(DomainBase.class, domainRepoId),
|
||||
billingEventOneTime,
|
||||
billingEventOneTimeHistoryId);
|
||||
}
|
||||
if (billingEventRecurring != null && !billingEventRecurring.maybeGetOfyKey().isPresent()) {
|
||||
billingEventRecurring =
|
||||
DomainBase.restoreOfyFrom(
|
||||
Key.create(DomainBase.class, domainRepoId),
|
||||
billingEventRecurring,
|
||||
billingEventRecurringHistoryId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,9 +111,9 @@ public class AllocationToken extends BackupGroupRoot implements Buildable, Datas
|
||||
@Nullable
|
||||
@Index
|
||||
@AttributeOverrides({
|
||||
@AttributeOverride(name = "domainRepoId", column = @Column(name = "redemption_domain_repo_id")),
|
||||
@AttributeOverride(name = "repoId", column = @Column(name = "redemption_domain_repo_id")),
|
||||
@AttributeOverride(
|
||||
name = "domainHistoryId",
|
||||
name = "historyRevisionId",
|
||||
column = @Column(name = "redemption_domain_history_id"))
|
||||
})
|
||||
DomainHistoryVKey redemptionHistoryEntry;
|
||||
@@ -192,8 +192,9 @@ public class AllocationToken extends BackupGroupRoot implements Buildable, Datas
|
||||
return token;
|
||||
}
|
||||
|
||||
public Optional<VKey<HistoryEntry>> getRedemptionHistoryEntry() {
|
||||
return Optional.ofNullable(redemptionHistoryEntry);
|
||||
public Optional<VKey<? extends HistoryEntry>> getRedemptionHistoryEntry() {
|
||||
return Optional.ofNullable(
|
||||
redemptionHistoryEntry == null ? null : redemptionHistoryEntry.createDomainHistoryVKey());
|
||||
}
|
||||
|
||||
public boolean isRedeemed() {
|
||||
@@ -291,9 +292,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable, Datas
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRedemptionHistoryEntry(DomainHistoryVKey redemptionHistoryEntry) {
|
||||
public Builder setRedemptionHistoryEntry(VKey<? extends HistoryEntry> redemptionHistoryEntry) {
|
||||
checkArgumentNotNull(redemptionHistoryEntry, "Redemption history entry must not be null");
|
||||
getInstance().redemptionHistoryEntry =
|
||||
checkArgumentNotNull(redemptionHistoryEntry, "Redemption history entry must not be null");
|
||||
DomainHistoryVKey.create(redemptionHistoryEntry.getOfyKey());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
package google.registry.model.index;
|
||||
|
||||
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.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.TypeUtils.instantiate;
|
||||
|
||||
@@ -29,6 +31,7 @@ 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 com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
@@ -44,6 +47,8 @@ import google.registry.model.host.HostResource;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.schema.replay.DatastoreOnlyEntity;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@@ -76,13 +81,21 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
|
||||
public static class ForeignKeyHostIndex extends ForeignKeyIndex<HostResource>
|
||||
implements DatastoreOnlyEntity {}
|
||||
|
||||
static final ImmutableMap<Class<? extends EppResource>, Class<? extends ForeignKeyIndex<?>>>
|
||||
private static final ImmutableMap<
|
||||
Class<? extends EppResource>, Class<? extends ForeignKeyIndex<?>>>
|
||||
RESOURCE_CLASS_TO_FKI_CLASS =
|
||||
ImmutableMap.of(
|
||||
ContactResource.class, ForeignKeyContactIndex.class,
|
||||
DomainBase.class, ForeignKeyDomainIndex.class,
|
||||
HostResource.class, ForeignKeyHostIndex.class);
|
||||
|
||||
private static final ImmutableMap<Class<? extends EppResource>, String>
|
||||
RESOURCE_CLASS_TO_FKI_PROPERTY =
|
||||
ImmutableMap.of(
|
||||
ContactResource.class, "contactId",
|
||||
DomainBase.class, "fullyQualifiedDomainName",
|
||||
HostResource.class, "fullyQualifiedHostName");
|
||||
|
||||
@Id String foreignKey;
|
||||
|
||||
/**
|
||||
@@ -179,9 +192,42 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
|
||||
*/
|
||||
public static <E extends EppResource> ImmutableMap<String, ForeignKeyIndex<E>> load(
|
||||
Class<E> clazz, Iterable<String> foreignKeys, final DateTime now) {
|
||||
return ofy().load().type(mapToFkiClass(clazz)).ids(foreignKeys).entrySet().stream()
|
||||
.filter(e -> now.isBefore(e.getValue().deletionTime))
|
||||
.collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
if (tm().isOfy()) {
|
||||
return ofy().load().type(mapToFkiClass(clazz)).ids(foreignKeys).entrySet().stream()
|
||||
.filter(e -> now.isBefore(e.getValue().deletionTime))
|
||||
.collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
} else {
|
||||
String property = RESOURCE_CLASS_TO_FKI_PROPERTY.get(clazz);
|
||||
List<E> entities =
|
||||
tm().transact(
|
||||
() -> {
|
||||
String entityName =
|
||||
jpaTm().getEntityManager().getMetamodel().entity(clazz).getName();
|
||||
return jpaTm()
|
||||
.getEntityManager()
|
||||
.createQuery(
|
||||
String.format(
|
||||
"FROM %s WHERE %s IN :propertyValue and deletionTime > :now ",
|
||||
entityName, property),
|
||||
clazz)
|
||||
.setParameter("propertyValue", foreignKeys)
|
||||
.setParameter("now", now)
|
||||
.getResultList();
|
||||
});
|
||||
// We need to find and return the entities with the maximum deletionTime for each foreign key.
|
||||
return Multimaps.index(entities, EppResource::getForeignKey).asMap().entrySet().stream()
|
||||
.map(
|
||||
entry ->
|
||||
Maps.immutableEntry(
|
||||
entry.getKey(),
|
||||
entry.getValue().stream()
|
||||
.max(Comparator.comparing(EppResource::getDeletionTime))
|
||||
.get()))
|
||||
.collect(
|
||||
toImmutableMap(
|
||||
Map.Entry::getKey,
|
||||
entry -> create(entry.getValue(), entry.getValue().getDeletionTime())));
|
||||
}
|
||||
}
|
||||
|
||||
static final CacheLoader<Key<ForeignKeyIndex<?>>, Optional<ForeignKeyIndex<?>>> CACHE_LOADER =
|
||||
@@ -266,7 +312,7 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
|
||||
.filter(entry -> entry.getValue().isPresent())
|
||||
.filter(entry -> now.isBefore(entry.getValue().get().getDeletionTime()))
|
||||
.collect(
|
||||
ImmutableMap.toImmutableMap(
|
||||
toImmutableMap(
|
||||
entry -> entry.getKey().getName(),
|
||||
entry -> (ForeignKeyIndex<E>) entry.getValue().get()));
|
||||
return fkisFromCache;
|
||||
|
||||
@@ -42,8 +42,8 @@ import google.registry.model.translators.CidrAddressBlockTranslatorFactory;
|
||||
import google.registry.model.translators.CommitLogRevisionsTranslatorFactory;
|
||||
import google.registry.model.translators.CreateAutoTimestampTranslatorFactory;
|
||||
import google.registry.model.translators.CurrencyUnitTranslatorFactory;
|
||||
import google.registry.model.translators.DomainHistoryVKeyTranslatorFactory;
|
||||
import google.registry.model.translators.DurationTranslatorFactory;
|
||||
import google.registry.model.translators.EppHistoryVKeyTranslatorFactory;
|
||||
import google.registry.model.translators.InetAddressTranslatorFactory;
|
||||
import google.registry.model.translators.ReadableInstantUtcTranslatorFactory;
|
||||
import google.registry.model.translators.UpdateAutoTimestampTranslatorFactory;
|
||||
@@ -128,7 +128,7 @@ public class ObjectifyService {
|
||||
new CreateAutoTimestampTranslatorFactory(),
|
||||
new CurrencyUnitTranslatorFactory(),
|
||||
new DurationTranslatorFactory(),
|
||||
new DomainHistoryVKeyTranslatorFactory(),
|
||||
new EppHistoryVKeyTranslatorFactory(),
|
||||
new InetAddressTranslatorFactory(),
|
||||
new MoneyStringTranslatorFactory(),
|
||||
new ReadableInstantUtcTranslatorFactory(),
|
||||
|
||||
@@ -1,48 +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.model.translators;
|
||||
|
||||
import com.google.appengine.api.datastore.Key;
|
||||
import google.registry.persistence.DomainHistoryVKey;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Translator factory for {@link DomainHistoryVKey}. */
|
||||
public class DomainHistoryVKeyTranslatorFactory
|
||||
extends AbstractSimpleTranslatorFactory<DomainHistoryVKey, Key> {
|
||||
|
||||
public DomainHistoryVKeyTranslatorFactory() {
|
||||
super(DomainHistoryVKey.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
SimpleTranslator<DomainHistoryVKey, Key> createTranslator() {
|
||||
return new SimpleTranslator<DomainHistoryVKey, Key>() {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DomainHistoryVKey loadValue(@Nullable Key datastoreValue) {
|
||||
return datastoreValue == null
|
||||
? null
|
||||
: DomainHistoryVKey.create(com.googlecode.objectify.Key.create(datastoreValue));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Key saveValue(@Nullable DomainHistoryVKey pojoValue) {
|
||||
return pojoValue == null ? null : pojoValue.getOfyKey().getRaw();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// 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.model.translators;
|
||||
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static java.util.function.Function.identity;
|
||||
|
||||
import com.google.appengine.api.datastore.Key;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.persistence.BillingVKey.BillingEventVKey;
|
||||
import google.registry.persistence.BillingVKey.BillingRecurrenceVKey;
|
||||
import google.registry.persistence.DomainHistoryVKey;
|
||||
import google.registry.persistence.EppHistoryVKey;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Translator factory for {@link EppHistoryVKey}. */
|
||||
public class EppHistoryVKeyTranslatorFactory
|
||||
extends AbstractSimpleTranslatorFactory<EppHistoryVKey, Key> {
|
||||
|
||||
public EppHistoryVKeyTranslatorFactory() {
|
||||
super(EppHistoryVKey.class);
|
||||
}
|
||||
|
||||
// This map is used when we need to convert the raw Datastore key to its VKey instance. We have
|
||||
// one dedicated VKey class, e.g. DomainHistoryVKey, for each such kind of entity, and we need
|
||||
// a way to map the raw Datastore key to its VKey class. So, we use the kind path as the key of
|
||||
// the map, and the kind path is created by concatenating all the kind strings in a raw Datastore
|
||||
// key, e.g. the map key for ContactPollMessageVKey is "ContactResource/HistoryEntry/PollMessage".
|
||||
@VisibleForTesting
|
||||
static final ImmutableMap<String, Class<? extends EppHistoryVKey>> kindPathToVKeyClass =
|
||||
ImmutableSet.of(DomainHistoryVKey.class, BillingEventVKey.class, BillingRecurrenceVKey.class)
|
||||
.stream()
|
||||
.collect(toImmutableMap(EppHistoryVKeyTranslatorFactory::getKindPath, identity()));
|
||||
|
||||
/**
|
||||
* Gets the kind path string for the given {@link Class}.
|
||||
*
|
||||
* <p>This method calls the getKindPath method on an instance of the given {@link Class} to get
|
||||
* the kind path string.
|
||||
*/
|
||||
private static String getKindPath(Class<? extends EppHistoryVKey> clazz) {
|
||||
try {
|
||||
Constructor<?> constructor = clazz.getDeclaredConstructor();
|
||||
constructor.setAccessible(true);
|
||||
Object instance = constructor.newInstance();
|
||||
Method getKindPathMethod = EppHistoryVKey.class.getDeclaredMethod("getKindPath");
|
||||
getKindPathMethod.setAccessible(true);
|
||||
return (String) getKindPathMethod.invoke(instance);
|
||||
} catch (Throwable t) {
|
||||
throw new IllegalStateException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
SimpleTranslator<EppHistoryVKey, Key> createTranslator() {
|
||||
return new SimpleTranslator<EppHistoryVKey, Key>() {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public EppHistoryVKey loadValue(@Nullable Key datastoreValue) {
|
||||
if (datastoreValue == null) {
|
||||
return null;
|
||||
} else {
|
||||
com.googlecode.objectify.Key<?> ofyKey =
|
||||
com.googlecode.objectify.Key.create(datastoreValue);
|
||||
String kindPath = EppHistoryVKey.createKindPath(ofyKey);
|
||||
if (kindPathToVKeyClass.containsKey(kindPath)) {
|
||||
Class<? extends EppHistoryVKey> vKeyClass = kindPathToVKeyClass.get(kindPath);
|
||||
try {
|
||||
Method createVKeyMethod =
|
||||
vKeyClass.getDeclaredMethod("create", com.googlecode.objectify.Key.class);
|
||||
return (EppHistoryVKey) createVKeyMethod.invoke(null, ofyKey);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalStateException(
|
||||
"Missing static method create(com.googlecode.objectify.Key) on " + vKeyClass);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new IllegalStateException("Error invoking createVKey on " + vKeyClass, e);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Missing EppHistoryVKey implementation for kind path: " + kindPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Key saveValue(@Nullable EppHistoryVKey pojoValue) {
|
||||
return pojoValue == null ? null : pojoValue.createOfyKey().getRaw();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
134
core/src/main/java/google/registry/persistence/BillingVKey.java
Normal file
134
core/src/main/java/google/registry/persistence/BillingVKey.java
Normal file
@@ -0,0 +1,134 @@
|
||||
// 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 com.googlecode.objectify.Key;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.OneTime;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.AttributeOverride;
|
||||
import javax.persistence.AttributeOverrides;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
/** Base class for {@link BillingEvent}'s {@link VKey}. */
|
||||
@MappedSuperclass
|
||||
public abstract class BillingVKey<K> extends EppHistoryVKey<K, DomainBase> {
|
||||
Long billingId;
|
||||
|
||||
// Hibernate requires a default constructor.
|
||||
BillingVKey() {}
|
||||
|
||||
BillingVKey(String repoId, long historyRevisionId, long billingId) {
|
||||
super(repoId, historyRevisionId);
|
||||
this.billingId = billingId;
|
||||
}
|
||||
|
||||
Key<HistoryEntry> createHistoryEntryKey() {
|
||||
return Key.create(Key.create(DomainBase.class, repoId), HistoryEntry.class, historyRevisionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createSqlKey() {
|
||||
return billingId;
|
||||
}
|
||||
|
||||
/** VKey class for {@link BillingEvent.OneTime} that belongs to a {@link DomainBase} entity. */
|
||||
@Embeddable
|
||||
@AttributeOverrides({
|
||||
@AttributeOverride(name = "repoId", column = @Column(name = "billing_event_domain_repo_id")),
|
||||
@AttributeOverride(
|
||||
name = "historyRevisionId",
|
||||
column = @Column(name = "billing_event_history_id")),
|
||||
@AttributeOverride(name = "billingId", column = @Column(name = "billing_event_id"))
|
||||
})
|
||||
public static class BillingEventVKey extends BillingVKey<OneTime> {
|
||||
|
||||
// Hibernate requires this default constructor
|
||||
private BillingEventVKey() {}
|
||||
|
||||
private BillingEventVKey(String repoId, long historyRevisionId, long billingEventId) {
|
||||
super(repoId, historyRevisionId, billingEventId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key<OneTime> createOfyKey() {
|
||||
return Key.create(createHistoryEntryKey(), BillingEvent.OneTime.class, billingId);
|
||||
}
|
||||
|
||||
/** Creates a {@link BillingEventVKey} instance from the given {@link Key} instance. */
|
||||
public static BillingEventVKey create(@Nullable Key<BillingEvent.OneTime> ofyKey) {
|
||||
if (ofyKey == null) {
|
||||
return null;
|
||||
}
|
||||
long billingEventId = ofyKey.getId();
|
||||
long historyRevisionId = ofyKey.getParent().getId();
|
||||
String repoId = ofyKey.getParent().getParent().getName();
|
||||
return new BillingEventVKey(repoId, historyRevisionId, billingEventId);
|
||||
}
|
||||
|
||||
/** Creates a {@link BillingEventVKey} instance from the given {@link VKey} instance. */
|
||||
public static BillingEventVKey create(@Nullable VKey<BillingEvent.OneTime> vKey) {
|
||||
return vKey == null ? null : create(vKey.getOfyKey());
|
||||
}
|
||||
}
|
||||
|
||||
/** VKey class for {@link BillingEvent.Recurring} that belongs to a {@link DomainBase} entity. */
|
||||
@Embeddable
|
||||
@AttributeOverrides({
|
||||
@AttributeOverride(
|
||||
name = "repoId",
|
||||
column = @Column(name = "billing_recurrence_domain_repo_id")),
|
||||
@AttributeOverride(
|
||||
name = "historyRevisionId",
|
||||
column = @Column(name = "billing_recurrence_history_id")),
|
||||
@AttributeOverride(name = "billingId", column = @Column(name = "billing_recurrence_id"))
|
||||
})
|
||||
public static class BillingRecurrenceVKey extends BillingVKey<Recurring> {
|
||||
|
||||
// Hibernate requires this default constructor
|
||||
private BillingRecurrenceVKey() {}
|
||||
|
||||
private BillingRecurrenceVKey(String repoId, long historyRevisionId, long billingEventId) {
|
||||
super(repoId, historyRevisionId, billingEventId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key<Recurring> createOfyKey() {
|
||||
return Key.create(createHistoryEntryKey(), BillingEvent.Recurring.class, billingId);
|
||||
}
|
||||
|
||||
/** Creates a {@link BillingRecurrenceVKey} instance from the given {@link Key} instance. */
|
||||
public static BillingRecurrenceVKey create(@Nullable Key<BillingEvent.Recurring> ofyKey) {
|
||||
if (ofyKey == null) {
|
||||
return null;
|
||||
}
|
||||
long billingEventId = ofyKey.getId();
|
||||
long historyRevisionId = ofyKey.getParent().getId();
|
||||
String repoId = ofyKey.getParent().getParent().getName();
|
||||
return new BillingRecurrenceVKey(repoId, historyRevisionId, billingEventId);
|
||||
}
|
||||
|
||||
/** Creates a {@link BillingRecurrenceVKey} instance from the given {@link VKey} instance. */
|
||||
public static BillingRecurrenceVKey create(@Nullable VKey<BillingEvent.Recurring> vKey) {
|
||||
return vKey == null ? null : create(vKey.getOfyKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,45 +18,44 @@ import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.PostLoad;
|
||||
|
||||
/** {@link VKey} for {@link HistoryEntry} which parent is {@link DomainBase}. */
|
||||
@Embeddable
|
||||
public class DomainHistoryVKey extends VKey<HistoryEntry> {
|
||||
|
||||
private String domainRepoId;
|
||||
|
||||
private Long domainHistoryId;
|
||||
public class DomainHistoryVKey extends EppHistoryVKey<HistoryEntry, DomainBase> {
|
||||
|
||||
// Hibernate requires a default constructor
|
||||
private DomainHistoryVKey() {}
|
||||
|
||||
private DomainHistoryVKey(String domainRepoId, long domainHistoryId) {
|
||||
initWith(domainRepoId, domainHistoryId);
|
||||
private DomainHistoryVKey(String repoId, long historyRevisionId) {
|
||||
super(repoId, historyRevisionId);
|
||||
}
|
||||
|
||||
@PostLoad
|
||||
void postLoad() {
|
||||
initWith(domainRepoId, domainHistoryId);
|
||||
@Override
|
||||
public Object createSqlKey() {
|
||||
return new DomainHistoryId(repoId, historyRevisionId);
|
||||
}
|
||||
|
||||
private void initWith(String domainRepoId, long domainHistoryId) {
|
||||
this.kind = HistoryEntry.class;
|
||||
this.ofyKey =
|
||||
Key.create(Key.create(DomainBase.class, domainRepoId), HistoryEntry.class, domainHistoryId);
|
||||
this.sqlKey = new DomainHistoryId(domainRepoId, domainHistoryId);
|
||||
this.domainRepoId = domainRepoId;
|
||||
this.domainHistoryId = domainHistoryId;
|
||||
@Override
|
||||
public Key<HistoryEntry> createOfyKey() {
|
||||
return Key.create(Key.create(DomainBase.class, repoId), HistoryEntry.class, historyRevisionId);
|
||||
}
|
||||
|
||||
/** Creates {@link DomainHistoryVKey} from the given {@link Key} instance. */
|
||||
public static DomainHistoryVKey create(Key<HistoryEntry> ofyKey) {
|
||||
public static DomainHistoryVKey create(Key<? extends HistoryEntry> ofyKey) {
|
||||
checkArgumentNotNull(ofyKey, "ofyKey must be specified");
|
||||
String domainRepoId = ofyKey.getParent().getName();
|
||||
long domainHistoryId = ofyKey.getId();
|
||||
return new DomainHistoryVKey(domainRepoId, domainHistoryId);
|
||||
String repoId = ofyKey.getParent().getName();
|
||||
long historyRevisionId = ofyKey.getId();
|
||||
return new DomainHistoryVKey(repoId, historyRevisionId);
|
||||
}
|
||||
|
||||
public VKey<? extends HistoryEntry> createDomainHistoryVKey() {
|
||||
return VKey.create(
|
||||
DomainHistory.class,
|
||||
createSqlKey(),
|
||||
Key.create(Key.create(DomainBase.class, repoId), DomainHistory.class, historyRevisionId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
// 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 com.google.common.base.Joiner;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.util.TypeUtils.TypeInstantiator;
|
||||
import java.io.Serializable;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
/**
|
||||
* Base class for {@link VKey} which ofyKey has a {@link HistoryEntry} key as its parent and a key
|
||||
* for EPP resource as its grandparent.
|
||||
*
|
||||
* <p>For such a {@link VKey}, we need to provide two type parameters to indicate the type of {@link
|
||||
* VKey} itself and the type of EPP resource respectively.
|
||||
*
|
||||
* @param <K> type of the {@link VKey}
|
||||
* @param <E> type of the EPP resource that the key belongs to
|
||||
*/
|
||||
@MappedSuperclass
|
||||
@Access(AccessType.FIELD)
|
||||
public abstract class EppHistoryVKey<K, E extends EppResource> extends ImmutableObject
|
||||
implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3906580677709539818L;
|
||||
|
||||
String repoId;
|
||||
|
||||
Long historyRevisionId;
|
||||
|
||||
// Hibernate requires a default constructor.
|
||||
EppHistoryVKey() {}
|
||||
|
||||
EppHistoryVKey(String repoId, long historyRevisionId) {
|
||||
this.repoId = repoId;
|
||||
this.historyRevisionId = historyRevisionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the kind path for the ofyKey in this instance.
|
||||
*
|
||||
* <p>This method is only used reflectively by {@link EppHistoryVKeyTranslatorFactory} to get the
|
||||
* kind path for a given {@link EppHistoryVKey} instance so it is marked as a private method.
|
||||
*
|
||||
* @see #createKindPath(Key)
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private String getKindPath() {
|
||||
String eppKind = Key.getKind(new TypeInstantiator<E>(getClass()) {}.getExactType());
|
||||
String keyKind = Key.getKind(new TypeInstantiator<K>(getClass()) {}.getExactType());
|
||||
if (keyKind.equals(Key.getKind(HistoryEntry.class))) {
|
||||
return createKindPath(eppKind, keyKind);
|
||||
} else {
|
||||
return createKindPath(eppKind, Key.getKind(HistoryEntry.class), keyKind);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the kind path for the given ofyKey}.
|
||||
*
|
||||
* <p>The kind path is a string including all kind names(delimited by slash) of a hierarchical
|
||||
* {@link Key}, e.g., the kind path for BillingEvent.OneTime is "DomainBase/HistoryEntry/OneTime".
|
||||
*/
|
||||
@Nullable
|
||||
public static String createKindPath(@Nullable Key<?> ofyKey) {
|
||||
if (ofyKey == null) {
|
||||
return null;
|
||||
} else if (ofyKey.getParent() == null) {
|
||||
return ofyKey.getKind();
|
||||
} else {
|
||||
return createKindPath(createKindPath(ofyKey.getParent()), ofyKey.getKind());
|
||||
}
|
||||
}
|
||||
|
||||
private static String createKindPath(String... kinds) {
|
||||
return Joiner.on("/").join(kinds);
|
||||
}
|
||||
|
||||
/** Creates a {@link VKey} from this instance. */
|
||||
public VKey<K> createVKey() {
|
||||
Class<K> vKeyType = new TypeInstantiator<K>(getClass()) {}.getExactType();
|
||||
return VKey.create(vKeyType, createSqlKey(), createOfyKey());
|
||||
}
|
||||
|
||||
public abstract Object createSqlKey();
|
||||
|
||||
public abstract Key<K> createOfyKey();
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.keyring.kms.KmsModule;
|
||||
import google.registry.persistence.PersistenceModule.AppEngineJpaTm;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import google.registry.privileges.secretmanager.SecretManagerModule;
|
||||
import google.registry.util.UtilsModule;
|
||||
import javax.inject.Singleton;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
@@ -32,6 +33,7 @@ import javax.persistence.EntityManagerFactory;
|
||||
CredentialModule.class,
|
||||
KmsModule.class,
|
||||
PersistenceModule.class,
|
||||
SecretManagerModule.class,
|
||||
UtilsModule.class
|
||||
})
|
||||
public interface PersistenceComponent {
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.google.api.client.auth.oauth2.Credential;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
@@ -33,6 +34,11 @@ import google.registry.keyring.kms.KmsKeyring;
|
||||
import google.registry.persistence.transaction.CloudSqlCredentialSupplier;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import google.registry.persistence.transaction.JpaTransactionManagerImpl;
|
||||
import google.registry.privileges.secretmanager.SqlCredential;
|
||||
import google.registry.privileges.secretmanager.SqlCredentialStore;
|
||||
import google.registry.privileges.secretmanager.SqlUser;
|
||||
import google.registry.privileges.secretmanager.SqlUser.RobotId;
|
||||
import google.registry.privileges.secretmanager.SqlUser.RobotUser;
|
||||
import google.registry.tools.AuthModule.CloudSqlClientCredential;
|
||||
import google.registry.util.Clock;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -47,6 +53,8 @@ import org.hibernate.cfg.Environment;
|
||||
/** Dagger module class for the persistence layer. */
|
||||
@Module
|
||||
public class PersistenceModule {
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
// This name must be the same as the one defined in persistence.xml.
|
||||
public static final String PERSISTENCE_UNIT_NAME = "nomulus";
|
||||
public static final String HIKARI_CONNECTION_TIMEOUT = "hibernate.hikari.connectionTimeout";
|
||||
@@ -122,11 +130,17 @@ public class PersistenceModule {
|
||||
static JpaTransactionManager provideAppEngineJpaTm(
|
||||
@Config("cloudSqlUsername") String username,
|
||||
KmsKeyring kmsKeyring,
|
||||
SqlCredentialStore credentialStore,
|
||||
@PartialCloudSqlConfigs ImmutableMap<String, String> cloudSqlConfigs,
|
||||
Clock clock) {
|
||||
HashMap<String, String> overrides = Maps.newHashMap(cloudSqlConfigs);
|
||||
overrides.put(Environment.USER, username);
|
||||
overrides.put(Environment.PASS, kmsKeyring.getCloudSqlPassword());
|
||||
validateCredentialStore(
|
||||
credentialStore,
|
||||
new RobotUser(RobotId.NOMULUS),
|
||||
overrides.get(Environment.USER),
|
||||
overrides.get(Environment.PASS));
|
||||
return new JpaTransactionManagerImpl(create(overrides), clock);
|
||||
}
|
||||
|
||||
@@ -136,6 +150,7 @@ public class PersistenceModule {
|
||||
static JpaTransactionManager provideNomulusToolJpaTm(
|
||||
@Config("toolsCloudSqlUsername") String username,
|
||||
KmsKeyring kmsKeyring,
|
||||
SqlCredentialStore credentialStore,
|
||||
@PartialCloudSqlConfigs ImmutableMap<String, String> cloudSqlConfigs,
|
||||
@CloudSqlClientCredential Credential credential,
|
||||
Clock clock) {
|
||||
@@ -143,6 +158,11 @@ public class PersistenceModule {
|
||||
HashMap<String, String> overrides = Maps.newHashMap(cloudSqlConfigs);
|
||||
overrides.put(Environment.USER, username);
|
||||
overrides.put(Environment.PASS, kmsKeyring.getToolsCloudSqlPassword());
|
||||
validateCredentialStore(
|
||||
credentialStore,
|
||||
new RobotUser(RobotId.TOOL),
|
||||
overrides.get(Environment.USER),
|
||||
overrides.get(Environment.PASS));
|
||||
return new JpaTransactionManagerImpl(create(overrides), clock);
|
||||
}
|
||||
|
||||
@@ -150,6 +170,7 @@ public class PersistenceModule {
|
||||
@Singleton
|
||||
@SocketFactoryJpaTm
|
||||
static JpaTransactionManager provideSocketFactoryJpaTm(
|
||||
SqlCredentialStore credentialStore,
|
||||
@Config("beamCloudSqlUsername") String username,
|
||||
@Config("beamCloudSqlPassword") String password,
|
||||
@Config("beamHibernateHikariMaximumPoolSize") int hikariMaximumPoolSize,
|
||||
@@ -159,6 +180,12 @@ public class PersistenceModule {
|
||||
overrides.put(Environment.USER, username);
|
||||
overrides.put(Environment.PASS, password);
|
||||
overrides.put(HIKARI_MAXIMUM_POOL_SIZE, String.valueOf(hikariMaximumPoolSize));
|
||||
// TODO(b/175700623): consider assigning different logins to pipelines
|
||||
validateCredentialStore(
|
||||
credentialStore,
|
||||
new RobotUser(RobotId.NOMULUS),
|
||||
overrides.get(Environment.USER),
|
||||
overrides.get(Environment.PASS));
|
||||
return new JpaTransactionManagerImpl(create(overrides), clock);
|
||||
}
|
||||
|
||||
@@ -203,6 +230,30 @@ public class PersistenceModule {
|
||||
return emf;
|
||||
}
|
||||
|
||||
/** Verifies that the credential from the Secret Manager matches the one currently in use.
|
||||
*
|
||||
* <p>This is a helper for the transition to the Secret Manager, and will be removed once data
|
||||
* and permissions are properly set up for all projects.
|
||||
**/
|
||||
private static void validateCredentialStore(
|
||||
SqlCredentialStore credentialStore, SqlUser sqlUser, String login, String password) {
|
||||
try {
|
||||
SqlCredential credential = credentialStore.getCredential(sqlUser);
|
||||
if (!credential.login().equals(login)) {
|
||||
logger.atWarning().log(
|
||||
"Wrong login for %s. Expecting %s, found %s.",
|
||||
sqlUser.geUserName(), login, credential.login());
|
||||
return;
|
||||
}
|
||||
if (!credential.password().equals(password)) {
|
||||
logger.atWarning().log("Wrong password for %s.", sqlUser.geUserName());
|
||||
}
|
||||
logger.atWarning().log("Credentials in the kerying and the secret manager match.");
|
||||
} catch (Throwable e) {
|
||||
logger.atWarning().log(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** Dagger qualifier for {@link JpaTransactionManager} used for App Engine application. */
|
||||
@Qualifier
|
||||
@Documented
|
||||
|
||||
@@ -29,6 +29,11 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.index.EppResourceIndex;
|
||||
import google.registry.model.index.ForeignKeyIndex.ForeignKeyContactIndex;
|
||||
import google.registry.model.index.ForeignKeyIndex.ForeignKeyDomainIndex;
|
||||
import google.registry.model.index.ForeignKeyIndex.ForeignKeyHostIndex;
|
||||
import google.registry.persistence.JpaRetries;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.Clock;
|
||||
@@ -56,6 +61,18 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final Retrier retrier = new Retrier(new SystemSleeper(), 3);
|
||||
|
||||
// The entity of classes in this set will be simply ignored when passed to modification
|
||||
// operations, i.e. insert, put, update and delete. This is to help maintain a single code path
|
||||
// when we switch from ofy() to tm() for the database migration as we don't need have a condition
|
||||
// to exclude the Datastore specific entities when the underlying tm() is jpaTm().
|
||||
// TODO(b/176108270): Remove this property after database migration.
|
||||
private static final ImmutableSet<Class<? extends ImmutableObject>> IGNORED_ENTITY_CLASSES =
|
||||
ImmutableSet.of(
|
||||
EppResourceIndex.class,
|
||||
ForeignKeyContactIndex.class,
|
||||
ForeignKeyDomainIndex.class,
|
||||
ForeignKeyHostIndex.class);
|
||||
|
||||
// EntityManagerFactory is thread safe.
|
||||
private final EntityManagerFactory emf;
|
||||
private final Clock clock;
|
||||
@@ -228,6 +245,9 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
@Override
|
||||
public void insert(Object entity) {
|
||||
checkArgumentNotNull(entity, "entity must be specified");
|
||||
if (isEntityOfIgnoredClass(entity)) {
|
||||
return;
|
||||
}
|
||||
assertInTransaction();
|
||||
getEntityManager().persist(entity);
|
||||
transactionInfo.get().addUpdate(entity);
|
||||
@@ -253,6 +273,9 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
@Override
|
||||
public void put(Object entity) {
|
||||
checkArgumentNotNull(entity, "entity must be specified");
|
||||
if (isEntityOfIgnoredClass(entity)) {
|
||||
return;
|
||||
}
|
||||
assertInTransaction();
|
||||
getEntityManager().merge(entity);
|
||||
transactionInfo.get().addUpdate(entity);
|
||||
@@ -278,6 +301,9 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
@Override
|
||||
public void update(Object entity) {
|
||||
checkArgumentNotNull(entity, "entity must be specified");
|
||||
if (isEntityOfIgnoredClass(entity)) {
|
||||
return;
|
||||
}
|
||||
assertInTransaction();
|
||||
checkArgument(exists(entity), "Given entity does not exist");
|
||||
getEntityManager().merge(entity);
|
||||
@@ -414,6 +440,9 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
@Override
|
||||
public void delete(Object entity) {
|
||||
checkArgumentNotNull(entity, "entity must be specified");
|
||||
if (isEntityOfIgnoredClass(entity)) {
|
||||
return;
|
||||
}
|
||||
assertInTransaction();
|
||||
Object managedEntity = entity;
|
||||
if (!getEntityManager().contains(entity)) {
|
||||
@@ -464,6 +493,10 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isEntityOfIgnoredClass(Object entity) {
|
||||
return IGNORED_ENTITY_CLASSES.contains(entity.getClass());
|
||||
}
|
||||
|
||||
private static ImmutableSet<EntityId> getEntityIdsFromEntity(
|
||||
EntityType<?> entityType, Object entity) {
|
||||
if (entityType.hasSingleIdAttribute()) {
|
||||
|
||||
@@ -16,13 +16,13 @@ package google.registry.privileges.secretmanager;
|
||||
|
||||
|
||||
import com.google.cloud.secretmanager.v1.SecretManagerServiceClient;
|
||||
import dagger.Component;
|
||||
import com.google.cloud.secretmanager.v1.SecretManagerServiceSettings;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.CredentialModule.DefaultCredential;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.util.UtilsModule;
|
||||
import java.io.IOException;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@@ -32,20 +32,29 @@ public abstract class SecretManagerModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static SecretManagerClient provideSecretManagerClient(
|
||||
@Config("projectId") String project, Retrier retrier) {
|
||||
static SecretManagerServiceSettings provideSecretManagerSetting(
|
||||
@DefaultCredential GoogleCredentialsBundle credentialsBundle) {
|
||||
try {
|
||||
SecretManagerServiceClient stub = SecretManagerServiceClient.create();
|
||||
return SecretManagerServiceSettings.newBuilder()
|
||||
.setCredentialsProvider(() -> credentialsBundle.getGoogleCredentials())
|
||||
.build();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static SecretManagerClient provideSecretManagerClient(
|
||||
SecretManagerServiceSettings serviceSettings,
|
||||
@Config("projectId") String project,
|
||||
Retrier retrier) {
|
||||
try {
|
||||
SecretManagerServiceClient stub = SecretManagerServiceClient.create(serviceSettings);
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(stub::close));
|
||||
return new SecretManagerClientImpl(project, stub, retrier);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {ConfigModule.class, SecretManagerModule.class, UtilsModule.class})
|
||||
public interface SecretManagerComponent {
|
||||
SecretManagerClient secretManagerClient();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,12 @@ public abstract class SqlUser {
|
||||
|
||||
/** Enumerates the {@link RobotUser RobotUsers} in the system. */
|
||||
public enum RobotId {
|
||||
NOMULUS;
|
||||
NOMULUS,
|
||||
/**
|
||||
* Credential for RegistryTool. This is temporary, and will be removed when tool users are
|
||||
* assigned their personal credentials.
|
||||
*/
|
||||
TOOL;
|
||||
}
|
||||
|
||||
/** Information of a RobotUser for privilege management purposes. */
|
||||
|
||||
@@ -67,7 +67,6 @@ public class Spec11RegistrarThreatMatchesParser {
|
||||
if (!gcsUtils.existsAndNotEmpty(spec11ReportFilename)) {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
ImmutableSet.Builder<RegistrarThreatMatches> builder = ImmutableSet.builder();
|
||||
try (InputStream in = gcsUtils.openInputStream(spec11ReportFilename);
|
||||
InputStreamReader isr = new InputStreamReader(in, UTF_8)) {
|
||||
// Skip the header at line 0
|
||||
|
||||
@@ -14,19 +14,19 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Iterables.partition;
|
||||
import static com.google.common.collect.Streams.stream;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -48,7 +48,7 @@ final class DeleteAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
||||
private static final int BATCH_SIZE = 20;
|
||||
private static final Joiner JOINER = Joiner.on(", ");
|
||||
|
||||
private ImmutableSet<Key<AllocationToken>> tokensToDelete;
|
||||
private ImmutableSet<VKey<AllocationToken>> tokensToDelete;
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
@@ -71,26 +71,24 @@ final class DeleteAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
||||
}
|
||||
|
||||
/** Deletes a (filtered) batch of AllocationTokens and returns how many were deleted. */
|
||||
private long deleteBatch(List<Key<AllocationToken>> batch) {
|
||||
private long deleteBatch(List<VKey<AllocationToken>> batch) {
|
||||
// Load the tokens in the same transaction as they are deleted to verify they weren't redeemed
|
||||
// since the query ran. This also filters out per-domain tokens if they're not to be deleted.
|
||||
ImmutableSet<AllocationToken> tokensToDelete =
|
||||
ofy().load().keys(batch).values().stream()
|
||||
.filter(t -> withDomains || !t.getDomainName().isPresent())
|
||||
ImmutableSet<VKey<AllocationToken>> tokensToDelete =
|
||||
tm().load(batch).values().stream()
|
||||
.filter(t -> withDomains || t.getDomainName().isEmpty())
|
||||
.filter(t -> SINGLE_USE.equals(t.getTokenType()))
|
||||
.filter(t -> !t.isRedeemed())
|
||||
.map(AllocationToken::createVKey)
|
||||
.collect(toImmutableSet());
|
||||
if (!dryRun) {
|
||||
ofy().delete().entities(tokensToDelete);
|
||||
tm().delete(tokensToDelete);
|
||||
}
|
||||
System.out.printf(
|
||||
"%s tokens: %s\n",
|
||||
dryRun ? "Would delete" : "Deleted",
|
||||
JOINER.join(
|
||||
tokensToDelete.stream()
|
||||
.map(AllocationToken::getToken)
|
||||
.sorted()
|
||||
.collect(toImmutableSet())));
|
||||
tokensToDelete.stream().map(VKey::getSqlKey).sorted().collect(toImmutableList())));
|
||||
return tokensToDelete.size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ import static com.google.common.collect.Queues.newArrayDeque;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
import static google.registry.util.StringGenerator.DEFAULT_PASSWORD_LENGTH;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
@@ -38,10 +38,10 @@ import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.io.Files;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.tools.params.TransitionListParameter.TokenStatusTransitions;
|
||||
import google.registry.util.CollectionUtils;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
@@ -258,8 +258,13 @@ class GenerateAllocationTokensCommand implements CommandWithRemoteApi {
|
||||
|
||||
@VisibleForTesting
|
||||
int saveTokens(final ImmutableSet<AllocationToken> tokens) {
|
||||
Collection<AllocationToken> savedTokens =
|
||||
dryRun ? tokens : tm().transact(() -> ofy().save().entities(tokens).now().values());
|
||||
Collection<AllocationToken> savedTokens;
|
||||
if (dryRun) {
|
||||
savedTokens = tokens;
|
||||
} else {
|
||||
transactIfJpaTm(() -> tm().transact(() -> tm().putAll(tokens)));
|
||||
savedTokens = tm().transact(() -> tm().loadAll(tokens));
|
||||
}
|
||||
savedTokens.forEach(
|
||||
t -> System.out.println(SKIP_NULLS.join(t.getDomainName().orElse(null), t.getToken())));
|
||||
return savedTokens.size();
|
||||
@@ -282,12 +287,14 @@ class GenerateAllocationTokensCommand implements CommandWithRemoteApi {
|
||||
}
|
||||
|
||||
private ImmutableSet<String> getExistingTokenStrings(ImmutableSet<String> candidates) {
|
||||
ImmutableSet<Key<AllocationToken>> existingTokenKeys =
|
||||
ImmutableSet<VKey<AllocationToken>> existingTokenKeys =
|
||||
candidates.stream()
|
||||
.map(input -> Key.create(AllocationToken.class, input))
|
||||
.map(input -> VKey.create(AllocationToken.class, input))
|
||||
.collect(toImmutableSet());
|
||||
return ofy().load().keys(existingTokenKeys).values().stream()
|
||||
.map(AllocationToken::getToken)
|
||||
.collect(toImmutableSet());
|
||||
return transactIfJpaTm(
|
||||
() ->
|
||||
tm().load(existingTokenKeys).values().stream()
|
||||
.map(AllocationToken::getToken)
|
||||
.collect(toImmutableSet()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
@@ -26,6 +25,7 @@ import com.google.common.collect.Lists;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -45,22 +45,26 @@ final class GetAllocationTokenCommand implements CommandWithRemoteApi {
|
||||
public void run() {
|
||||
ImmutableMap.Builder<String, AllocationToken> builder = new ImmutableMap.Builder<>();
|
||||
for (List<String> tokens : Lists.partition(mainParameters, BATCH_SIZE)) {
|
||||
ImmutableList<Key<AllocationToken>> tokenKeys =
|
||||
tokens.stream().map(t -> Key.create(AllocationToken.class, t)).collect(toImmutableList());
|
||||
ofy().load().keys(tokenKeys).forEach((k, v) -> builder.put(k.getName(), v));
|
||||
ImmutableList<VKey<AllocationToken>> tokenKeys =
|
||||
tokens.stream()
|
||||
.map(t -> VKey.create(AllocationToken.class, t))
|
||||
.collect(toImmutableList());
|
||||
tm().load(tokenKeys).forEach((k, v) -> builder.put(k.getSqlKey().toString(), v));
|
||||
}
|
||||
ImmutableMap<String, AllocationToken> loadedTokens = builder.build();
|
||||
ImmutableMap<Key<DomainBase>, DomainBase> domains = loadRedeemedDomains(loadedTokens.values());
|
||||
ImmutableMap<VKey<DomainBase>, DomainBase> domains = loadRedeemedDomains(loadedTokens.values());
|
||||
|
||||
for (String token : mainParameters) {
|
||||
if (loadedTokens.containsKey(token)) {
|
||||
AllocationToken loadedToken = loadedTokens.get(token);
|
||||
System.out.println(loadedToken.toString());
|
||||
if (!loadedToken.getRedemptionHistoryEntry().isPresent()) {
|
||||
if (loadedToken.getRedemptionHistoryEntry().isEmpty()) {
|
||||
System.out.printf("Token %s was not redeemed.\n", token);
|
||||
} else {
|
||||
Key<DomainBase> domainOfyKey =
|
||||
loadedToken.getRedemptionHistoryEntry().get().getOfyKey().getParent();
|
||||
DomainBase domain =
|
||||
domains.get(loadedToken.getRedemptionHistoryEntry().get().getOfyKey().getParent());
|
||||
domains.get(VKey.create(DomainBase.class, domainOfyKey.getName(), domainOfyKey));
|
||||
if (domain == null) {
|
||||
System.out.printf("ERROR: Token %s was redeemed but domain can't be loaded.\n", token);
|
||||
} else {
|
||||
@@ -76,19 +80,23 @@ final class GetAllocationTokenCommand implements CommandWithRemoteApi {
|
||||
}
|
||||
}
|
||||
|
||||
private static ImmutableMap<Key<DomainBase>, DomainBase> loadRedeemedDomains(
|
||||
@SuppressWarnings("unchecked")
|
||||
private static ImmutableMap<VKey<DomainBase>, DomainBase> loadRedeemedDomains(
|
||||
Collection<AllocationToken> tokens) {
|
||||
ImmutableList<Key<DomainBase>> domainKeys =
|
||||
ImmutableList<VKey<DomainBase>> domainKeys =
|
||||
tokens.stream()
|
||||
.map(AllocationToken::getRedemptionHistoryEntry)
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.map(key -> tm().load(key))
|
||||
.map(he -> (Key<DomainBase>) he.getParent())
|
||||
.map(key -> VKey.create(DomainBase.class, key.getName(), key))
|
||||
.collect(toImmutableList());
|
||||
ImmutableMap.Builder<Key<DomainBase>, DomainBase> domainsBuilder = new ImmutableMap.Builder<>();
|
||||
for (List<Key<DomainBase>> keys : Lists.partition(domainKeys, BATCH_SIZE)) {
|
||||
domainsBuilder.putAll(ofy().load().keys(keys));
|
||||
ImmutableMap.Builder<VKey<DomainBase>, DomainBase> domainsBuilder =
|
||||
new ImmutableMap.Builder<>();
|
||||
for (List<VKey<DomainBase>> keys : Lists.partition(domainKeys, BATCH_SIZE)) {
|
||||
tm().load(ImmutableList.copyOf(keys))
|
||||
.forEach((k, v) -> domainsBuilder.put((VKey<DomainBase>) k, v));
|
||||
}
|
||||
return domainsBuilder.build();
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Iterables.partition;
|
||||
import static com.google.common.collect.Streams.stream;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
@@ -105,13 +105,17 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
||||
}
|
||||
|
||||
tokensToSave =
|
||||
ofy().load().keys(getTokenKeys()).values().stream()
|
||||
.collect(toImmutableMap(Function.identity(), this::updateToken))
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> !entry.getKey().equals(entry.getValue())) // only update changed tokens
|
||||
.map(Map.Entry::getValue)
|
||||
.collect(toImmutableSet());
|
||||
transactIfJpaTm(
|
||||
() ->
|
||||
tm().load(getTokenKeys()).values().stream()
|
||||
.collect(toImmutableMap(Function.identity(), this::updateToken))
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(
|
||||
entry ->
|
||||
!entry.getKey().equals(entry.getValue())) // only update changed tokens
|
||||
.map(Map.Entry::getValue)
|
||||
.collect(toImmutableSet()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -123,7 +127,7 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
||||
protected String execute() {
|
||||
long numUpdated =
|
||||
stream(partition(tokensToSave, BATCH_SIZE))
|
||||
.mapToLong(batch -> tm().transact(() -> saveBatch(batch)))
|
||||
.mapToLong(batch -> tm().transact(() -> saveBatch(ImmutableList.copyOf(batch))))
|
||||
.sum();
|
||||
return String.format("Updated %d tokens in total.", numUpdated);
|
||||
}
|
||||
@@ -141,9 +145,9 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private long saveBatch(List<AllocationToken> batch) {
|
||||
private long saveBatch(ImmutableList<AllocationToken> batch) {
|
||||
if (!dryRun) {
|
||||
ofy().save().entities(batch);
|
||||
tm().putAll(batch);
|
||||
}
|
||||
System.out.printf(
|
||||
"%s tokens: %s\n",
|
||||
|
||||
@@ -15,13 +15,15 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.List;
|
||||
|
||||
/** Shared base class for commands to update or delete allocation tokens. */
|
||||
@@ -47,19 +49,28 @@ abstract class UpdateOrDeleteAllocationTokensCommand extends ConfirmingCommand
|
||||
description = "Do not actually update or delete the tokens; defaults to false")
|
||||
protected boolean dryRun;
|
||||
|
||||
protected ImmutableSet<Key<AllocationToken>> getTokenKeys() {
|
||||
protected ImmutableSet<VKey<AllocationToken>> getTokenKeys() {
|
||||
checkArgument(
|
||||
tokens == null ^ prefix == null,
|
||||
"Must provide one of --tokens or --prefix, not both / neither");
|
||||
if (tokens != null) {
|
||||
return tokens.stream()
|
||||
.map(token -> Key.create(AllocationToken.class, token))
|
||||
.collect(toImmutableSet());
|
||||
ImmutableSet<VKey<AllocationToken>> keys =
|
||||
tokens.stream()
|
||||
.map(token -> VKey.create(AllocationToken.class, token))
|
||||
.collect(toImmutableSet());
|
||||
ImmutableSet<VKey<AllocationToken>> nonexistentKeys =
|
||||
transactIfJpaTm(
|
||||
() -> keys.stream().filter(key -> !tm().exists(key)).collect(toImmutableSet()));
|
||||
checkState(nonexistentKeys.isEmpty(), "Tokens with keys %s did not exist.", nonexistentKeys);
|
||||
return keys;
|
||||
} else {
|
||||
checkArgument(!prefix.isEmpty(), "Provided prefix should not be blank");
|
||||
return ofy().load().type(AllocationToken.class).keys().list().stream()
|
||||
.filter(key -> key.getName().startsWith(prefix))
|
||||
.collect(toImmutableSet());
|
||||
return transactIfJpaTm(
|
||||
() ->
|
||||
tm().loadAll(AllocationToken.class).stream()
|
||||
.filter(token -> token.getToken().startsWith(prefix))
|
||||
.map(AllocationToken::createVKey)
|
||||
.collect(toImmutableSet()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,19 @@ class ResaveAllEppResourcesActionTest extends MapreduceTestCase<ResaveAllEppReso
|
||||
.isGreaterThan(creationTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_fastMode_doesNotResaveEntityWithNoChanges() throws Exception {
|
||||
ContactResource contact = persistActiveContact("test123");
|
||||
DateTime creationTime = contact.getUpdateTimestamp().getTimestamp();
|
||||
assertThat(ofy().load().entity(contact).now().getUpdateTimestamp().getTimestamp())
|
||||
.isEqualTo(creationTime);
|
||||
ofy().clearSessionCache();
|
||||
action.isFast = true;
|
||||
runMapreduce();
|
||||
assertThat(ofy().load().entity(contact).now().getUpdateTimestamp().getTimestamp())
|
||||
.isEqualTo(creationTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_mapreduceResolvesPendingTransfer() throws Exception {
|
||||
DateTime now = DateTime.now(UTC);
|
||||
|
||||
@@ -100,7 +100,11 @@ public abstract class FlowTestCase<F extends Flow> {
|
||||
|
||||
@RegisterExtension
|
||||
final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withDatastoreAndCloudSql().withTaskQueue().build();
|
||||
AppEngineExtension.builder()
|
||||
.withClock(clock)
|
||||
.withDatastoreAndCloudSql()
|
||||
.withTaskQueue()
|
||||
.build();
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEachFlowTestCase() {
|
||||
@@ -288,7 +292,7 @@ public abstract class FlowTestCase<F extends Flow> {
|
||||
e);
|
||||
}
|
||||
// Clear the cache so that we don't see stale results in tests.
|
||||
ofy().clearSessionCache();
|
||||
tm().clearSessionCache();
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.model.tmch.ClaimsListShardTest.createTestClaimsListShard;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
|
||||
import static google.registry.testing.LogsSubject.assertAboutLogs;
|
||||
import static google.registry.testing.TaskQueueHelper.assertTasksEnqueued;
|
||||
@@ -72,7 +74,7 @@ public abstract class ResourceFlowTestCase<F extends Flow, R extends EppResource
|
||||
protected R reloadResourceByForeignKey(DateTime now) throws Exception {
|
||||
// Force the session to be cleared so that when we read it back, we read from Datastore and not
|
||||
// from the transaction's session cache.
|
||||
ofy().clearSessionCache();
|
||||
tm().clearSessionCache();
|
||||
return loadByForeignKey(getResourceClass(), getUniqueIdFromCommand(), now).orElse(null);
|
||||
}
|
||||
|
||||
@@ -83,9 +85,9 @@ public abstract class ResourceFlowTestCase<F extends Flow, R extends EppResource
|
||||
|
||||
protected <T extends EppResource> T reloadResourceAndCloneAtTime(T resource, DateTime now) {
|
||||
// Force the session to be cleared.
|
||||
ofy().clearSessionCache();
|
||||
tm().clearSessionCache();
|
||||
@SuppressWarnings("unchecked")
|
||||
T refreshedResource = (T) ofy().load().entity(resource).now().cloneProjectedAtTime(now);
|
||||
T refreshedResource = (T) transactIfJpaTm(() -> tm().load(resource)).cloneProjectedAtTime(now);
|
||||
return refreshedResource;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,6 @@ import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.Registry.TldState;
|
||||
import google.registry.model.registry.label.ReservedList;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.DomainHistoryVKey;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -169,7 +168,7 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRedemptionHistoryEntry(DomainHistoryVKey.create(historyEntryKey))
|
||||
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey))
|
||||
.build());
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "In use"),
|
||||
|
||||
@@ -162,7 +162,6 @@ import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.monitoring.whitebox.EppMetric;
|
||||
import google.registry.persistence.DomainHistoryVKey;
|
||||
import google.registry.testing.ReplayExtension;
|
||||
import google.registry.testing.TaskQueueHelper.TaskMatcher;
|
||||
import java.math.BigDecimal;
|
||||
@@ -505,7 +504,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRedemptionHistoryEntry(DomainHistoryVKey.create(historyEntryKey))
|
||||
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey))
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
EppException thrown =
|
||||
@@ -528,7 +527,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
HistoryEntry historyEntry =
|
||||
ofy().load().type(HistoryEntry.class).ancestor(reloadResourceByForeignKey()).first().now();
|
||||
assertThat(ofy().load().entity(token).now().getRedemptionHistoryEntry())
|
||||
.hasValue(DomainHistoryVKey.create(Key.create(historyEntry)));
|
||||
.hasValue(HistoryEntry.createVKey(Key.create(historyEntry)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1275,7 +1274,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertThat(reloadedToken.isRedeemed()).isTrue();
|
||||
assertThat(reloadedToken.getRedemptionHistoryEntry())
|
||||
.hasValue(
|
||||
DomainHistoryVKey.create(
|
||||
HistoryEntry.createVKey(
|
||||
Key.create(getHistoryEntries(reloadResourceByForeignKey()).get(0))));
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.DomainHistoryVKey;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -198,7 +197,7 @@ class AllocationTokenFlowUtilsTest {
|
||||
new AllocationToken.Builder()
|
||||
.setToken("tokeN")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRedemptionHistoryEntry(DomainHistoryVKey.create(historyEntryKey))
|
||||
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey))
|
||||
.build());
|
||||
assertThat(
|
||||
flowUtils
|
||||
|
||||
@@ -24,16 +24,18 @@ import google.registry.flows.EppException;
|
||||
import google.registry.flows.ResourceCheckFlowTestCase;
|
||||
import google.registry.flows.exceptions.TooManyResourceChecksException;
|
||||
import google.registry.model.host.HostResource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
|
||||
/** Unit tests for {@link HostCheckFlow}. */
|
||||
@DualDatabaseTest
|
||||
class HostCheckFlowTest extends ResourceCheckFlowTestCase<HostCheckFlow, HostResource> {
|
||||
|
||||
HostCheckFlowTest() {
|
||||
setEppInput("host_check.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNothingExists() throws Exception {
|
||||
// These ids come from the check xml.
|
||||
doCheckTest(
|
||||
@@ -42,7 +44,7 @@ class HostCheckFlowTest extends ResourceCheckFlowTestCase<HostCheckFlow, HostRes
|
||||
create(true, "ns3.example.tld", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testOneExists() throws Exception {
|
||||
persistActiveHost("ns1.example.tld");
|
||||
// These ids come from the check xml.
|
||||
@@ -52,7 +54,7 @@ class HostCheckFlowTest extends ResourceCheckFlowTestCase<HostCheckFlow, HostRes
|
||||
create(true, "ns3.example.tld", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testOneExistsButWasDeleted() throws Exception {
|
||||
persistDeletedHost("ns1.example.tld", clock.nowUtc().minusDays(1));
|
||||
// These ids come from the check xml.
|
||||
@@ -62,27 +64,27 @@ class HostCheckFlowTest extends ResourceCheckFlowTestCase<HostCheckFlow, HostRes
|
||||
create(true, "ns3.example.tld", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testXmlMatches() throws Exception {
|
||||
persistActiveHost("ns2.example.tld");
|
||||
runFlowAssertResponse(loadFile("host_check_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test50IdsAllowed() throws Exception {
|
||||
// Make sure we don't have a regression that reduces the number of allowed checks.
|
||||
setEppInput("host_check_50.xml");
|
||||
runFlow();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testTooManyIds() {
|
||||
setEppInput("host_check_51.xml");
|
||||
EppException thrown = assertThrows(TooManyResourceChecksException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testIcannActivityReportField_getsLogged() throws Exception {
|
||||
runFlow();
|
||||
assertIcannReportingActivityFieldLogged("srs-host-check");
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.flows.host;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.createTlds;
|
||||
@@ -53,10 +54,12 @@ import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link HostCreateFlow}. */
|
||||
@DualDatabaseTest
|
||||
class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResource> {
|
||||
|
||||
private void setEppHostCreateInput(String hostName, String hostAddrs) {
|
||||
@@ -90,7 +93,9 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
|
||||
.hasOnlyOneHistoryEntryWhich()
|
||||
.hasType(HistoryEntry.Type.HOST_CREATE);
|
||||
assertNoBillingEvents();
|
||||
assertEppResourceIndexEntityFor(reloadResourceByForeignKey());
|
||||
if (tm().isOfy()) {
|
||||
assertEppResourceIndexEntityFor(reloadResourceByForeignKey());
|
||||
}
|
||||
}
|
||||
|
||||
private void doSuccessfulInternalTest(String tld) throws Exception {
|
||||
@@ -100,19 +105,19 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
|
||||
doSuccessfulTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testDryRun() throws Exception {
|
||||
dryRunFlowAssertResponse(loadFile("host_create_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_externalNeverExisted() throws Exception {
|
||||
doSuccessfulTest();
|
||||
assertAboutHosts().that(reloadResourceByForeignKey()).hasSuperordinateDomain(null);
|
||||
assertNoDnsTasksEnqueued();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_internalNeverExisted() throws Exception {
|
||||
doSuccessfulInternalTest("tld");
|
||||
HostResource host = reloadResourceByForeignKey();
|
||||
@@ -123,7 +128,7 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
|
||||
assertDnsTasksEnqueued("ns1.example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_multipartTLDsAndInvalidHost() {
|
||||
createTlds("bar.tld", "tld");
|
||||
|
||||
@@ -132,7 +137,7 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_externalExistedButWasDeleted() throws Exception {
|
||||
persistDeletedHost(getUniqueIdFromCommand(), clock.nowUtc().minusDays(1));
|
||||
doSuccessfulTest();
|
||||
@@ -140,7 +145,7 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
|
||||
assertNoDnsTasksEnqueued();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_internalExistedButWasDeleted() throws Exception {
|
||||
persistDeletedHost(getUniqueIdFromCommand(), clock.nowUtc().minusDays(1));
|
||||
doSuccessfulInternalTest("tld");
|
||||
@@ -152,7 +157,7 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
|
||||
assertDnsTasksEnqueued("ns1.example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_subordinateNeedsIps() {
|
||||
setEppHostCreateInput("ns1.example.tld", null);
|
||||
createTld("tld");
|
||||
@@ -161,7 +166,7 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_externalMustNotHaveIps() {
|
||||
setEppHostCreateInputWithIps("ns1.example.external");
|
||||
createTld("tld");
|
||||
@@ -170,7 +175,7 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_superordinateMissing() {
|
||||
setEppHostCreateInput("ns1.example.tld", null);
|
||||
createTld("tld");
|
||||
@@ -179,7 +184,7 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
|
||||
assertThat(thrown).hasMessageThat().contains("(example.tld)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_superordinateInPendingDelete() {
|
||||
setEppHostCreateInputWithIps("ns1.example.tld");
|
||||
createTld("tld");
|
||||
@@ -197,7 +202,7 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
|
||||
.contains("Superordinate domain for this hostname is in pending delete");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_alreadyExists() throws Exception {
|
||||
setEppHostCreateInput("ns1.example.tld", null);
|
||||
persistActiveHost(getUniqueIdFromCommand());
|
||||
@@ -209,7 +214,7 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
|
||||
String.format("Object with given ID (%s) already exists", getUniqueIdFromCommand()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_resourceContention() throws Exception {
|
||||
setEppHostCreateInput("ns1.example.tld", null);
|
||||
String targetId = getUniqueIdFromCommand();
|
||||
@@ -226,14 +231,14 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_nonLowerCaseHostname() {
|
||||
setEppHostCreateInput("ns1.EXAMPLE.tld", null);
|
||||
EppException thrown = assertThrows(HostNameNotLowerCaseException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_nonPunyCodedHostname() {
|
||||
setEppHostCreateInput("ns1.çauçalito.みんな", null);
|
||||
HostNameNotPunyCodedException thrown =
|
||||
@@ -241,21 +246,21 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
|
||||
assertThat(thrown).hasMessageThat().contains("expected ns1.xn--aualito-txac.xn--q9jyb4c");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_nonCanonicalHostname() {
|
||||
setEppHostCreateInput("ns1.example.tld.", null);
|
||||
EppException thrown = assertThrows(HostNameNotNormalizedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_longHostName() {
|
||||
setEppHostCreateInputWithIps("a" + Strings.repeat(".labelpart", 25) + ".tld");
|
||||
EppException thrown = assertThrows(HostNameTooLongException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_ip4AddressWithIp6Declaration() {
|
||||
setEppHostCreateInput(
|
||||
"ns1.example.tld",
|
||||
@@ -272,37 +277,37 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_badCharacter() {
|
||||
doFailingHostNameTest("foo bar", InvalidHostNameException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_tooShallowPublicSuffix() {
|
||||
doFailingHostNameTest("example.tld", HostNameTooShallowException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_tooShallowCcTld() {
|
||||
doFailingHostNameTest("foo.co.uk", HostNameTooShallowException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_barePublicSuffix() {
|
||||
doFailingHostNameTest("com", HostNameTooShallowException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_bareCcTld() {
|
||||
doFailingHostNameTest("co.uk", HostNameTooShallowException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_tooShallowNewTld() {
|
||||
doFailingHostNameTest("example.lol", HostNameTooShallowException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_ccTldInBailiwick() {
|
||||
createTld("co.uk");
|
||||
setEppHostCreateInputWithIps("foo.co.uk");
|
||||
@@ -310,7 +315,7 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testIcannActivityReportField_getsLogged() throws Exception {
|
||||
runFlow();
|
||||
assertIcannReportingActivityFieldLogged("srs-host-create");
|
||||
|
||||
@@ -26,66 +26,68 @@ import google.registry.flows.host.HostFlowUtils.HostNameTooLongException;
|
||||
import google.registry.flows.host.HostFlowUtils.HostNameTooShallowException;
|
||||
import google.registry.flows.host.HostFlowUtils.InvalidHostNameException;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link HostFlowUtils}. */
|
||||
@DualDatabaseTest
|
||||
class HostFlowUtilsTest {
|
||||
|
||||
@RegisterExtension
|
||||
final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withDatastoreAndCloudSql().build();
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_validExternalHostName_validates() throws Exception {
|
||||
assertThat(validateHostName("host.example.com").toString()).isEqualTo("host.example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_validExternalHostNameOnRegistrySuffixList_validates() throws Exception {
|
||||
assertThat(validateHostName("host.blogspot.com").toString()).isEqualTo("host.blogspot.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_validExternalHostNameOnRegistrySuffixList_multipartTLD_validates() throws Exception {
|
||||
assertThat(validateHostName("ns1.host.co.uk").toString()).isEqualTo("ns1.host.co.uk");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_validExternalHostNameOnRegistrySuffixList_multipartTLD_tooShallow() {
|
||||
assertThrows(
|
||||
HostNameTooShallowException.class, () -> validateHostName("host.co.uk").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_validateHostName_hostNameTooLong() {
|
||||
assertThrows(
|
||||
HostNameTooLongException.class,
|
||||
() -> validateHostName(Strings.repeat("na", 200) + ".wat.man"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_validateHostName_hostNameNotLowerCase() {
|
||||
assertThrows(HostNameNotLowerCaseException.class, () -> validateHostName("NA.CAPS.TLD"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_validateHostName_hostNameNotPunyCoded() {
|
||||
assertThrows(
|
||||
HostNameNotPunyCodedException.class, () -> validateHostName("motörhead.death.metal"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_validateHostName_hostNameNotNormalized() {
|
||||
assertThrows(HostNameNotNormalizedException.class, () -> validateHostName("root.node.yeah."));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_validateHostName_hostNameHasLeadingHyphen() {
|
||||
assertThrows(InvalidHostNameException.class, () -> validateHostName("-giga.mega.tld"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_validateHostName_hostNameTooShallow() {
|
||||
assertThrows(HostNameTooShallowException.class, () -> validateHostName("domain.tld"));
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.googlecode.objectify.annotation.Parent;
|
||||
import com.googlecode.objectify.annotation.Serialize;
|
||||
import com.googlecode.objectify.cmd.Query;
|
||||
import google.registry.model.ofy.Ofy;
|
||||
import google.registry.persistence.EppHistoryVKey;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
@@ -173,7 +174,8 @@ public abstract class EntityTestCase {
|
||||
}
|
||||
// Descend into persisted ImmutableObject classes, but not anything else.
|
||||
if (ImmutableObject.class.isAssignableFrom(fieldClass)
|
||||
&& !VKey.class.isAssignableFrom(fieldClass)) {
|
||||
&& !VKey.class.isAssignableFrom(fieldClass)
|
||||
&& !EppHistoryVKey.class.isAssignableFrom(fieldClass)) {
|
||||
getAllPotentiallyIndexedFieldPaths(fieldClass).stream()
|
||||
.map(subfield -> field.getName() + "." + subfield)
|
||||
.distinct()
|
||||
|
||||
@@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadAtPointInTime;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.newHostResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistNewRegistrars;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistResourceWithCommitLog;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
@@ -26,16 +27,19 @@ import static org.joda.time.DateTimeZone.UTC;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.ofy.Ofy;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import google.registry.testing.TestOfyOnly;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Tests for {@link EppResourceUtils}. */
|
||||
public class EppResourceUtilsTest {
|
||||
@DualDatabaseTest
|
||||
class EppResourceUtilsTest {
|
||||
|
||||
@RegisterExtension
|
||||
public final AppEngineExtension appEngine =
|
||||
@@ -51,7 +55,7 @@ public class EppResourceUtilsTest {
|
||||
inject.setStaticField(Ofy.class, "clock", clock);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testLoadAtPointInTime_beforeCreated_returnsNull() {
|
||||
clock.advanceOneMilli();
|
||||
// Don't save a commit log, we shouldn't need one.
|
||||
@@ -62,7 +66,7 @@ public class EppResourceUtilsTest {
|
||||
assertThat(loadAtPointInTime(host, clock.nowUtc().minus(Duration.millis(1))).now()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testLoadAtPointInTime_atOrAfterLastAutoUpdateTime_returnsResource() {
|
||||
clock.advanceOneMilli();
|
||||
// Don't save a commit log, we shouldn't need one.
|
||||
@@ -73,8 +77,9 @@ public class EppResourceUtilsTest {
|
||||
assertThat(loadAtPointInTime(host, clock.nowUtc()).now()).isEqualTo(host);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyOnly
|
||||
void testLoadAtPointInTime_usingIntactRevisionHistory_returnsMutationValue() {
|
||||
persistNewRegistrars("OLD", "NEW");
|
||||
clock.advanceOneMilli();
|
||||
// Save resource with a commit log that we can read in later as a revisions map value.
|
||||
HostResource oldHost = persistResourceWithCommitLog(
|
||||
@@ -94,7 +99,7 @@ public class EppResourceUtilsTest {
|
||||
.isEqualTo(oldHost);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyOnly
|
||||
void testLoadAtPointInTime_brokenRevisionHistory_returnsResourceAsIs() {
|
||||
// Don't save a commit log since we want to test the handling of a broken revisions key.
|
||||
HostResource oldHost = persistResource(
|
||||
@@ -114,7 +119,7 @@ public class EppResourceUtilsTest {
|
||||
assertThat(loadAtPointInTime(host, clock.nowUtc().minusMillis(1)).now()).isEqualTo(host);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyOnly
|
||||
void testLoadAtPointInTime_fallback_returnsMutationValueForOldestRevision() {
|
||||
clock.advanceOneMilli();
|
||||
// Save a commit log that we can fall back to.
|
||||
@@ -136,7 +141,7 @@ public class EppResourceUtilsTest {
|
||||
.isEqualTo(oldHost);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyOnly
|
||||
void testLoadAtPointInTime_ultimateFallback_onlyOneRevision_returnsCurrentResource() {
|
||||
clock.advanceOneMilli();
|
||||
// Don't save a commit log; we want to test that we load from the current resource.
|
||||
@@ -151,7 +156,7 @@ public class EppResourceUtilsTest {
|
||||
assertThat(loadAtPointInTime(host, clock.nowUtc().minusMillis(1)).now()).isEqualTo(host);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyOnly
|
||||
void testLoadAtPointInTime_moreThanThirtyDaysInPast_historyIsPurged() {
|
||||
clock.advanceOneMilli();
|
||||
HostResource host =
|
||||
|
||||
@@ -633,14 +633,13 @@ public class DomainBaseSqlTest {
|
||||
"4-COM",
|
||||
END_OF_TIME,
|
||||
"registrar1",
|
||||
createLegacyVKey(
|
||||
BillingEvent.OneTime.class, oneTimeBillingEvent.getId())),
|
||||
oneTimeBillingEvent.createVKey()),
|
||||
GracePeriod.createForRecurring(
|
||||
GracePeriodStatus.AUTO_RENEW,
|
||||
"4-COM",
|
||||
END_OF_TIME,
|
||||
"registrar1",
|
||||
createLegacyVKey(BillingEvent.Recurring.class, billEvent.getId())));
|
||||
billEvent.createVKey()));
|
||||
|
||||
jpaTm().insert(contact);
|
||||
jpaTm().insert(contact2);
|
||||
|
||||
@@ -875,16 +875,13 @@ public class DomainBaseTest extends EntityTestCase {
|
||||
ImmutableSet<BillEventInfo> historyIds =
|
||||
domain.getGracePeriods().stream()
|
||||
.map(
|
||||
gp ->
|
||||
new BillEventInfo(
|
||||
gp.getRecurringBillingEvent(), gp.billingEventRecurringHistoryId,
|
||||
gp.getOneTimeBillingEvent(), gp.billingEventOneTimeHistoryId))
|
||||
gp -> new BillEventInfo(gp.getRecurringBillingEvent(), gp.getOneTimeBillingEvent()))
|
||||
.collect(toImmutableSet());
|
||||
assertThat(historyIds)
|
||||
.isEqualTo(
|
||||
ImmutableSet.of(
|
||||
new BillEventInfo(null, null, oneTimeBillKey, historyEntryKey.getId()),
|
||||
new BillEventInfo(recurringBillKey, historyEntryKey.getId(), null, null)));
|
||||
new BillEventInfo(null, oneTimeBillKey),
|
||||
new BillEventInfo(recurringBillKey, null)));
|
||||
}
|
||||
|
||||
static class BillEventInfo extends ImmutableObject {
|
||||
@@ -895,13 +892,9 @@ public class DomainBaseTest extends EntityTestCase {
|
||||
|
||||
BillEventInfo(
|
||||
VKey<BillingEvent.Recurring> billingEventRecurring,
|
||||
Long billingEventRecurringHistoryId,
|
||||
VKey<BillingEvent.OneTime> billingEventOneTime,
|
||||
Long billingEventOneTimeHistoryId) {
|
||||
VKey<BillingEvent.OneTime> billingEventOneTime) {
|
||||
this.billingEventRecurring = billingEventRecurring;
|
||||
this.billingEventRecurringHistoryId = billingEventRecurringHistoryId;
|
||||
this.billingEventOneTime = billingEventOneTime;
|
||||
this.billingEventOneTimeHistoryId = billingEventOneTimeHistoryId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,11 +63,11 @@ public class GracePeriodTest {
|
||||
recurringKey =
|
||||
VKey.create(
|
||||
Recurring.class,
|
||||
12345,
|
||||
12345L,
|
||||
Key.create(
|
||||
Key.create(Key.create(DomainBase.class, "1-TEST"), HistoryEntry.class, 343L),
|
||||
Recurring.class,
|
||||
12345));
|
||||
12345L));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,8 +80,6 @@ public class GracePeriodTest {
|
||||
assertThat(gracePeriod.getClientId()).isEqualTo("TheRegistrar");
|
||||
assertThat(gracePeriod.getExpirationTime()).isEqualTo(now.plusDays(1));
|
||||
assertThat(gracePeriod.hasBillingEvent()).isTrue();
|
||||
assertThat(gracePeriod.billingEventOneTimeHistoryId).isEqualTo(12345L);
|
||||
assertThat(gracePeriod.billingEventRecurringHistoryId).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -96,8 +94,6 @@ public class GracePeriodTest {
|
||||
assertThat(gracePeriod.getClientId()).isEqualTo("TheRegistrar");
|
||||
assertThat(gracePeriod.getExpirationTime()).isEqualTo(now.plusDays(1));
|
||||
assertThat(gracePeriod.hasBillingEvent()).isTrue();
|
||||
assertThat(gracePeriod.billingEventOneTimeHistoryId).isNull();
|
||||
assertThat(gracePeriod.billingEventRecurringHistoryId).isEqualTo(343L);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -16,15 +16,14 @@ package google.registry.model.domain.token;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.CANCELLED;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.ENDED;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.NOT_STARTED;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.VALID;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
@@ -40,13 +39,14 @@ import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.DomainHistoryVKey;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import google.registry.testing.TestOfyOnly;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link AllocationToken}. */
|
||||
@DualDatabaseTest
|
||||
public class AllocationTokenTest extends EntityTestCase {
|
||||
|
||||
public AllocationTokenTest() {
|
||||
@@ -58,7 +58,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
createTld("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testPersistence() {
|
||||
AllocationToken unlimitedUseToken =
|
||||
persistResource(
|
||||
@@ -78,7 +78,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
.put(DateTime.now(UTC).plusWeeks(8), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
assertThat(ofy().load().entity(unlimitedUseToken).now()).isEqualTo(unlimitedUseToken);
|
||||
assertThat(transactIfJpaTm(() -> tm().load(unlimitedUseToken))).isEqualTo(unlimitedUseToken);
|
||||
|
||||
DomainBase domain = persistActiveDomain("example.foo");
|
||||
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1);
|
||||
@@ -86,37 +86,15 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123Single")
|
||||
.setRedemptionHistoryEntry(DomainHistoryVKey.create(historyEntryKey))
|
||||
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey))
|
||||
.setDomainName("example.foo")
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setTokenType(SINGLE_USE)
|
||||
.build());
|
||||
assertThat(ofy().load().entity(singleUseToken).now()).isEqualTo(singleUseToken);
|
||||
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
jpaTm().insert(unlimitedUseToken);
|
||||
jpaTm().insert(singleUseToken);
|
||||
});
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
assertAboutImmutableObjects()
|
||||
.that(jpaTm().load(VKey.createSql(AllocationToken.class, "abc123Unlimited")))
|
||||
.isEqualExceptFields(
|
||||
unlimitedUseToken,
|
||||
"creationTime",
|
||||
"updateTimestamp",
|
||||
"redemptionHistoryEntry");
|
||||
assertAboutImmutableObjects()
|
||||
.that(jpaTm().load(VKey.createSql(AllocationToken.class, "abc123Single")))
|
||||
.isEqualExceptFields(
|
||||
singleUseToken, "creationTime", "updateTimestamp", "redemptionHistoryEntry");
|
||||
});
|
||||
assertThat(transactIfJpaTm(() -> tm().load(singleUseToken))).isEqualTo(singleUseToken);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyOnly
|
||||
void testIndexing() throws Exception {
|
||||
DomainBase domain = persistActiveDomain("blahdomain.foo");
|
||||
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1);
|
||||
@@ -125,7 +103,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRedemptionHistoryEntry(DomainHistoryVKey.create(historyEntryKey))
|
||||
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey))
|
||||
.setDomainName("blahdomain.foo")
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.build()),
|
||||
@@ -134,7 +112,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
"domainName");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testCreationTime_autoPopulates() {
|
||||
AllocationToken tokenBeforePersisting =
|
||||
new AllocationToken.Builder().setToken("abc123").setTokenType(SINGLE_USE).build();
|
||||
@@ -143,7 +121,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
assertThat(tokenAfterPersisting.getCreationTime()).hasValue(fakeClock.nowUtc());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSetCreationTime_cantCallMoreThanOnce() {
|
||||
AllocationToken.Builder builder =
|
||||
new AllocationToken.Builder()
|
||||
@@ -157,7 +135,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Creation time can only be set once");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSetToken_cantCallMoreThanOnce() {
|
||||
AllocationToken.Builder builder = new AllocationToken.Builder().setToken("foobar");
|
||||
IllegalStateException thrown =
|
||||
@@ -165,7 +143,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Token can only be set once");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSetTokenType_cantCallMoreThanOnce() {
|
||||
AllocationToken.Builder builder =
|
||||
new AllocationToken.Builder().setTokenType(TokenType.UNLIMITED_USE);
|
||||
@@ -174,7 +152,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Token type can only be set once");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testBuild_DomainNameWithLessThanTwoParts() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -192,7 +170,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Invalid domain name: example");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testBuild_invalidTld() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -210,7 +188,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Invalid domain name: example.nosuchtld");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testBuild_domainNameOnlyOnSingleUse() {
|
||||
AllocationToken.Builder builder =
|
||||
new AllocationToken.Builder()
|
||||
@@ -223,7 +201,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
.isEqualTo("Domain name can only be specified for SINGLE_USE tokens");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testBuild_redemptionHistoryEntryOnlyInSingleUse() {
|
||||
DomainBase domain = persistActiveDomain("blahdomain.foo");
|
||||
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1);
|
||||
@@ -231,14 +209,14 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
new AllocationToken.Builder()
|
||||
.setToken("foobar")
|
||||
.setTokenType(TokenType.UNLIMITED_USE)
|
||||
.setRedemptionHistoryEntry(DomainHistoryVKey.create(historyEntryKey));
|
||||
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey));
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Redemption history entry can only be specified for SINGLE_USE tokens");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSetTransitions_notStartOfTime() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -256,7 +234,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
.isEqualTo("tokenStatusTransitions map must start at START_OF_TIME.");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSetTransitions_badInitialValue() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -273,14 +251,14 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
.isEqualTo("tokenStatusTransitions must start with NOT_STARTED");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSetTransitions_invalidInitialTransitions() {
|
||||
// NOT_STARTED can only go to VALID or CANCELLED
|
||||
assertBadInitialTransition(NOT_STARTED);
|
||||
assertBadInitialTransition(ENDED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSetTransitions_badTransitionsFromValid() {
|
||||
// VALID can only go to ENDED or CANCELLED
|
||||
assertBadTransition(
|
||||
@@ -301,14 +279,14 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
NOT_STARTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSetTransitions_terminalTransitions() {
|
||||
// both ENDED and CANCELLED are terminal
|
||||
assertTerminal(ENDED);
|
||||
assertTerminal(CANCELLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSetDiscountFractionTooHigh() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -319,7 +297,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
.isEqualTo("Discount fraction must be between 0 and 1 inclusive");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSetDiscountFractionTooLow() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -330,7 +308,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
.isEqualTo("Discount fraction must be between 0 and 1 inclusive");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSetDiscountYearsTooHigh() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -341,7 +319,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
.isEqualTo("Discount years must be between 1 and 10 inclusive");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSetDiscountYearsTooLow() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -352,7 +330,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
.isEqualTo("Discount years must be between 1 and 10 inclusive");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testBuild_noTokenType() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -361,7 +339,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Token type must be specified");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testBuild_noToken() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -370,7 +348,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Token must not be null or empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testBuild_emptyToken() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -379,7 +357,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Token must not be blank");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testBuild_discountPremiumsRequiresDiscountFraction() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -395,7 +373,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
.isEqualTo("Discount premiums can only be specified along with a discount fraction");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testBuild_discountYearsRequiresDiscountFraction() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
|
||||
@@ -20,6 +20,7 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.deleteResource;
|
||||
import static google.registry.testing.DatabaseHelper.newDomainBase;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistDeletedHost;
|
||||
@@ -29,16 +30,21 @@ import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.EntityTestCase;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.index.ForeignKeyIndex.ForeignKeyHostIndex;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.TestCacheExtension;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import google.registry.testing.TestOfyOnly;
|
||||
import google.registry.testing.TestSqlOnly;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link ForeignKeyIndex}. */
|
||||
public class ForeignKeyIndexTest extends EntityTestCase {
|
||||
@DualDatabaseTest
|
||||
class ForeignKeyIndexTest extends EntityTestCase {
|
||||
|
||||
@RegisterExtension
|
||||
public final TestCacheExtension testCacheExtension =
|
||||
@@ -49,7 +55,17 @@ public class ForeignKeyIndexTest extends EntityTestCase {
|
||||
createTld("com");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSqlOnly
|
||||
void testModifyForeignKeyIndex_notThrowExceptionInSql() {
|
||||
DomainBase domainBase = newDomainBase("test.com");
|
||||
ForeignKeyIndex<DomainBase> fki = ForeignKeyIndex.create(domainBase, fakeClock.nowUtc());
|
||||
tm().transact(() -> tm().insert(fki));
|
||||
tm().transact(() -> tm().put(fki));
|
||||
tm().transact(() -> tm().delete(fki));
|
||||
tm().transact(() -> tm().update(fki));
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testPersistence() {
|
||||
// Persist a host and implicitly persist a ForeignKeyIndex for it.
|
||||
HostResource host = persistActiveHost("ns1.example.com");
|
||||
@@ -59,7 +75,7 @@ public class ForeignKeyIndexTest extends EntityTestCase {
|
||||
assertThat(fki.getDeletionTime()).isEqualTo(END_OF_TIME);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyOnly
|
||||
void testIndexing() throws Exception {
|
||||
// Persist a host and implicitly persist a ForeignKeyIndex for it.
|
||||
persistActiveHost("ns1.example.com");
|
||||
@@ -68,38 +84,50 @@ public class ForeignKeyIndexTest extends EntityTestCase {
|
||||
"deletionTime");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testLoadForNonexistentForeignKey_returnsNull() {
|
||||
assertThat(ForeignKeyIndex.load(HostResource.class, "ns1.example.com", fakeClock.nowUtc()))
|
||||
.isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testLoadForDeletedForeignKey_returnsNull() {
|
||||
HostResource host = persistActiveHost("ns1.example.com");
|
||||
persistResource(ForeignKeyIndex.create(host, fakeClock.nowUtc().minusDays(1)));
|
||||
if (tm().isOfy()) {
|
||||
persistResource(ForeignKeyIndex.create(host, fakeClock.nowUtc().minusDays(1)));
|
||||
} else {
|
||||
persistResource(host.asBuilder().setDeletionTime(fakeClock.nowUtc().minusDays(1)).build());
|
||||
}
|
||||
assertThat(ForeignKeyIndex.load(HostResource.class, "ns1.example.com", fakeClock.nowUtc()))
|
||||
.isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testLoad_newerKeyHasBeenSoftDeleted() {
|
||||
HostResource host1 = persistActiveHost("ns1.example.com");
|
||||
fakeClock.advanceOneMilli();
|
||||
ForeignKeyHostIndex fki = new ForeignKeyHostIndex();
|
||||
fki.foreignKey = "ns1.example.com";
|
||||
fki.topReference = host1.createVKey();
|
||||
fki.deletionTime = fakeClock.nowUtc();
|
||||
persistResource(fki);
|
||||
if (tm().isOfy()) {
|
||||
ForeignKeyHostIndex fki = new ForeignKeyHostIndex();
|
||||
fki.foreignKey = "ns1.example.com";
|
||||
fki.topReference = host1.createVKey();
|
||||
fki.deletionTime = fakeClock.nowUtc();
|
||||
persistResource(fki);
|
||||
} else {
|
||||
persistResource(host1.asBuilder().setDeletionTime(fakeClock.nowUtc()).build());
|
||||
}
|
||||
assertThat(ForeignKeyIndex.load(HostResource.class, "ns1.example.com", fakeClock.nowUtc()))
|
||||
.isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testBatchLoad_skipsDeletedAndNonexistent() {
|
||||
persistActiveHost("ns1.example.com");
|
||||
HostResource host = persistActiveHost("ns2.example.com");
|
||||
persistResource(ForeignKeyIndex.create(host, fakeClock.nowUtc().minusDays(1)));
|
||||
if (tm().isOfy()) {
|
||||
persistResource(ForeignKeyIndex.create(host, fakeClock.nowUtc().minusDays(1)));
|
||||
} else {
|
||||
persistResource(host.asBuilder().setDeletionTime(fakeClock.nowUtc().minusDays(1)).build());
|
||||
}
|
||||
assertThat(
|
||||
ForeignKeyIndex.load(
|
||||
HostResource.class,
|
||||
@@ -109,7 +137,7 @@ public class ForeignKeyIndexTest extends EntityTestCase {
|
||||
.containsExactly("ns1.example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testDeadCodeThatDeletedScrapCommandsReference() {
|
||||
persistActiveHost("omg");
|
||||
assertThat(ForeignKeyIndex.load(HostResource.class, "omg", fakeClock.nowUtc()).getForeignKey())
|
||||
@@ -124,7 +152,7 @@ public class ForeignKeyIndexTest extends EntityTestCase {
|
||||
return ForeignKeyIndex.load(ContactResource.class, contactId, fakeClock.nowUtc());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyOnly
|
||||
void test_loadCached_cachesNonexistenceOfHosts() {
|
||||
assertThat(
|
||||
ForeignKeyIndex.loadCached(
|
||||
@@ -144,7 +172,7 @@ public class ForeignKeyIndexTest extends EntityTestCase {
|
||||
.containsExactly("ns4.example.com", loadHostFki("ns4.example.com"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyOnly
|
||||
void test_loadCached_cachesExistenceOfHosts() {
|
||||
HostResource host1 = persistActiveHost("ns1.example.com");
|
||||
HostResource host2 = persistActiveHost("ns2.example.com");
|
||||
@@ -172,7 +200,7 @@ public class ForeignKeyIndexTest extends EntityTestCase {
|
||||
"ns3.example.com", loadHostFki("ns3.example.com"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyOnly
|
||||
void test_loadCached_doesntSeeHostChangesWhileCacheIsValid() {
|
||||
HostResource originalHost = persistActiveHost("ns1.example.com");
|
||||
ForeignKeyIndex<HostResource> originalFki = loadHostFki("ns1.example.com");
|
||||
@@ -195,7 +223,7 @@ public class ForeignKeyIndexTest extends EntityTestCase {
|
||||
.containsExactly("ns1.example.com", originalFki);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyOnly
|
||||
void test_loadCached_filtersOutSoftDeletedHosts() {
|
||||
persistActiveHost("ns1.example.com");
|
||||
persistDeletedHost("ns2.example.com", fakeClock.nowUtc().minusDays(1));
|
||||
@@ -207,7 +235,7 @@ public class ForeignKeyIndexTest extends EntityTestCase {
|
||||
.containsExactly("ns1.example.com", loadHostFki("ns1.example.com"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyOnly
|
||||
void test_loadCached_cachesContactFkis() {
|
||||
persistActiveContact("contactid1");
|
||||
ForeignKeyIndex<ContactResource> fki1 = loadContactFki("contactid1");
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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.model.translators;
|
||||
|
||||
import static google.registry.model.translators.EppHistoryVKeyTranslatorFactory.kindPathToVKeyClass;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit test for {@link EppHistoryVKeyTranslatorFactory}. */
|
||||
class EppHistoryVKeyTranslatorFactoryTest {
|
||||
|
||||
@Test
|
||||
void assertAllVKeyClassesHavingCreateFromOfyKeyMethod() {
|
||||
kindPathToVKeyClass.forEach(
|
||||
(kindPath, vKeyClass) -> {
|
||||
try {
|
||||
vKeyClass.getDeclaredMethod("create", com.googlecode.objectify.Key.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
fail("Missing static method create(com.googlecode.objectify.Key) on " + vKeyClass, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
// 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 static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
import com.googlecode.objectify.annotation.Parent;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.common.EntityGroupRoot;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.BillingVKey.BillingEventVKey;
|
||||
import google.registry.persistence.BillingVKey.BillingRecurrenceVKey;
|
||||
import google.registry.schema.replay.EntityTest.EntityForTesting;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import javax.persistence.Transient;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit test for {@link BillingVKey}. */
|
||||
@DualDatabaseTest
|
||||
class BillingVKeyTest {
|
||||
@RegisterExtension
|
||||
final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder()
|
||||
.withDatastoreAndCloudSql()
|
||||
.withOfyTestEntities(BillingVKeyTestEntity.class)
|
||||
.withJpaUnitTestEntities(BillingVKeyTestEntity.class)
|
||||
.build();
|
||||
|
||||
@TestOfyAndSql
|
||||
void testRestoreSymmetricVKey() {
|
||||
Key<HistoryEntry> domainHistoryKey =
|
||||
Key.create(Key.create(DomainBase.class, "domainRepoId"), HistoryEntry.class, 10L);
|
||||
|
||||
Key<BillingEvent.OneTime> oneTimeOfyKey =
|
||||
Key.create(domainHistoryKey, BillingEvent.OneTime.class, 100L);
|
||||
VKey<BillingEvent.OneTime> oneTimeVKey =
|
||||
VKey.create(BillingEvent.OneTime.class, 100L, oneTimeOfyKey);
|
||||
|
||||
Key<BillingEvent.Recurring> recurringOfyKey =
|
||||
Key.create(domainHistoryKey, BillingEvent.Recurring.class, 200L);
|
||||
VKey<BillingEvent.Recurring> recurringVKey =
|
||||
VKey.create(BillingEvent.Recurring.class, 200L, recurringOfyKey);
|
||||
|
||||
BillingVKeyTestEntity original = new BillingVKeyTestEntity(oneTimeVKey, recurringVKey);
|
||||
tm().transact(() -> tm().insert(original));
|
||||
BillingVKeyTestEntity persisted = tm().transact(() -> tm().load(original.createVKey()));
|
||||
|
||||
assertThat(persisted).isEqualTo(original);
|
||||
assertThat(persisted.getBillingEventVKey()).isEqualTo(oneTimeVKey);
|
||||
assertThat(persisted.getBillingRecurrenceVKey()).isEqualTo(recurringVKey);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testHandleNullVKeyCorrectly() {
|
||||
BillingVKeyTestEntity original = new BillingVKeyTestEntity(null, null);
|
||||
tm().transact(() -> tm().insert(original));
|
||||
BillingVKeyTestEntity persisted = tm().transact(() -> tm().load(original.createVKey()));
|
||||
|
||||
assertThat(persisted).isEqualTo(original);
|
||||
}
|
||||
|
||||
@EntityForTesting
|
||||
@Entity
|
||||
@javax.persistence.Entity
|
||||
private static class BillingVKeyTestEntity extends ImmutableObject {
|
||||
@Transient @Parent Key<EntityGroupRoot> parent = getCrossTldKey();
|
||||
|
||||
@Id @javax.persistence.Id String id = "id";
|
||||
|
||||
BillingEventVKey billingEventVKey;
|
||||
|
||||
BillingRecurrenceVKey billingRecurrenceVKey;
|
||||
|
||||
BillingVKeyTestEntity() {}
|
||||
|
||||
BillingVKeyTestEntity(
|
||||
VKey<BillingEvent.OneTime> onetime, VKey<BillingEvent.Recurring> recurring) {
|
||||
this.billingEventVKey = BillingEventVKey.create(onetime);
|
||||
this.billingRecurrenceVKey = BillingRecurrenceVKey.create(recurring);
|
||||
}
|
||||
|
||||
VKey<BillingEvent.OneTime> getBillingEventVKey() {
|
||||
return billingEventVKey.createVKey();
|
||||
}
|
||||
|
||||
VKey<BillingEvent.Recurring> getBillingRecurrenceVKey() {
|
||||
return billingRecurrenceVKey.createVKey();
|
||||
}
|
||||
|
||||
VKey<BillingVKeyTestEntity> createVKey() {
|
||||
return VKey.create(
|
||||
BillingVKeyTestEntity.class, id, Key.create(parent, BillingVKeyTestEntity.class, id));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,12 +56,17 @@ class DomainHistoryVKeyTest {
|
||||
TestEntity persisted = tm().transact(() -> tm().load(original.createVKey()));
|
||||
assertThat(persisted).isEqualTo(original);
|
||||
// Double check that the persisted.domainHistoryVKey is a symmetric VKey
|
||||
assertThat(persisted.domainHistoryVKey.getKind()).isEqualTo(HistoryEntry.class);
|
||||
assertThat(persisted.domainHistoryVKey.getOfyKey())
|
||||
assertThat(persisted.domainHistoryVKey.createOfyKey())
|
||||
.isEqualTo(
|
||||
Key.create(Key.create(DomainBase.class, "domainRepoId"), HistoryEntry.class, 10L));
|
||||
assertThat(persisted.domainHistoryVKey.getSqlKey())
|
||||
assertThat(persisted.domainHistoryVKey.createSqlKey())
|
||||
.isEqualTo(new DomainHistoryId("domainRepoId", 10L));
|
||||
assertThat(persisted.domainHistoryVKey.createVKey())
|
||||
.isEqualTo(
|
||||
VKey.create(
|
||||
HistoryEntry.class,
|
||||
new DomainHistoryId("domainRepoId", 10L),
|
||||
Key.create(Key.create(DomainBase.class, "domainRepoId"), HistoryEntry.class, 10L)));
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
@@ -69,9 +74,12 @@ class DomainHistoryVKeyTest {
|
||||
Key<HistoryEntry> ofyKey =
|
||||
Key.create(Key.create(DomainBase.class, "domainRepoId"), HistoryEntry.class, 10L);
|
||||
DomainHistoryVKey domainHistoryVKey = DomainHistoryVKey.create(ofyKey);
|
||||
assertThat(domainHistoryVKey.getKind()).isEqualTo(HistoryEntry.class);
|
||||
assertThat(domainHistoryVKey.getOfyKey()).isEqualTo(ofyKey);
|
||||
assertThat(domainHistoryVKey.getSqlKey()).isEqualTo(new DomainHistoryId("domainRepoId", 10L));
|
||||
assertThat(domainHistoryVKey.createOfyKey()).isEqualTo(ofyKey);
|
||||
assertThat(domainHistoryVKey.createSqlKey())
|
||||
.isEqualTo(new DomainHistoryId("domainRepoId", 10L));
|
||||
assertThat(domainHistoryVKey.createVKey())
|
||||
.isEqualTo(
|
||||
VKey.create(HistoryEntry.class, new DomainHistoryId("domainRepoId", 10L), ofyKey));
|
||||
}
|
||||
|
||||
@EntityForTesting
|
||||
|
||||
@@ -17,6 +17,8 @@ package google.registry.privileges.secretmanager;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.auth.oauth2.GoogleCredentials;
|
||||
import com.google.cloud.secretmanager.v1.SecretManagerServiceSettings;
|
||||
import com.google.cloud.secretmanager.v1.SecretVersion.State;
|
||||
import google.registry.privileges.secretmanager.SecretManagerClient.NoSuchSecretResourceException;
|
||||
import google.registry.privileges.secretmanager.SecretManagerClient.SecretAlreadyExistsException;
|
||||
@@ -54,11 +56,14 @@ public class SecretManagerClientTest {
|
||||
private String secretId;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
static void beforeAll() throws IOException {
|
||||
String environmentName = System.getProperty("test.gcp_integration.env");
|
||||
if (environmentName != null) {
|
||||
secretManagerClient =
|
||||
SecretManagerModule.provideSecretManagerClient(
|
||||
SecretManagerServiceSettings.newBuilder()
|
||||
.setCredentialsProvider(() -> GoogleCredentials.getApplicationDefault())
|
||||
.build(),
|
||||
String.format("domain-registry-%s", environmentName),
|
||||
new Retrier(new SystemSleeper(), 1));
|
||||
isUnitTest = false;
|
||||
|
||||
@@ -454,10 +454,6 @@ public final class AppEngineExtension implements BeforeEachCallback, AfterEachCa
|
||||
.setEnvIsAdmin(userInfo.isAdmin());
|
||||
}
|
||||
|
||||
if (clock != null) {
|
||||
helper.setClock(() -> clock.nowUtc().getMillis());
|
||||
}
|
||||
|
||||
if (withLocalModules) {
|
||||
helper.setEnvInstance("0");
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.persistence.transaction.TransactionManager;
|
||||
import google.registry.persistence.transaction.TransactionManagerFactory;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -160,6 +161,14 @@ class DualDatabaseTestInvocationContextProvider implements TestTemplateInvocatio
|
||||
|
||||
private static boolean isDualDatabaseTest(ExtensionContext context) {
|
||||
Object testInstance = context.getTestInstance().orElseThrow(RuntimeException::new);
|
||||
return testInstance.getClass().isAnnotationPresent(DualDatabaseTest.class);
|
||||
// If the test method is declared in its parent class,
|
||||
// e.g. google.registry.flows.ResourceFlowTestCase.testRequiresLogin,
|
||||
// we don't consider it is a DualDatabaseTest. This is because there may exist some subclasses
|
||||
// that have not been migrated to DualDatabaseTest.
|
||||
boolean isDeclaredTestMethod =
|
||||
ImmutableSet.copyOf(testInstance.getClass().getDeclaredMethods())
|
||||
.contains(context.getTestMethod().orElseThrow(RuntimeException::new));
|
||||
return testInstance.getClass().isAnnotationPresent(DualDatabaseTest.class)
|
||||
&& isDeclaredTestMethod;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ package google.registry.tools;
|
||||
import static com.google.common.collect.Iterables.concat;
|
||||
import static com.google.common.collect.Iterables.toArray;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
@@ -116,7 +117,7 @@ public abstract class CommandTestCase<C extends Command> {
|
||||
} finally {
|
||||
// Clear the session cache so that subsequent reads for verification purposes hit Datastore.
|
||||
// This primarily matters for AutoTimestamp fields, which otherwise won't have updated values.
|
||||
ofy().clearSessionCache();
|
||||
tm().clearSessionCache();
|
||||
// Reset back to UNITTEST environment.
|
||||
RegistryToolEnvironment.UNITTEST.setup(systemPropertyExtension);
|
||||
}
|
||||
@@ -185,12 +186,12 @@ public abstract class CommandTestCase<C extends Command> {
|
||||
|
||||
/** Reloads the given resource from Datastore. */
|
||||
<T> T reloadResource(T resource) {
|
||||
return ofy().load().entity(resource).now();
|
||||
return transactIfJpaTm(() -> tm().load(resource));
|
||||
}
|
||||
|
||||
/** Returns count of all poll messages in Datastore. */
|
||||
int getPollMessageCount() {
|
||||
return ofy().load().type(PollMessage.class).count();
|
||||
return transactIfJpaTm(() -> tm().loadAll(PollMessage.class).size());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,24 +16,28 @@ package google.registry.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
import static google.registry.testing.DatabaseHelper.createTlds;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.DomainHistoryVKey;
|
||||
import java.util.Collection;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.Nullable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link DeleteAllocationTokensCommand}. */
|
||||
@DualDatabaseTest
|
||||
class DeleteAllocationTokensCommandTest extends CommandTestCase<DeleteAllocationTokensCommand> {
|
||||
|
||||
private AllocationToken preRed1;
|
||||
@@ -54,38 +58,38 @@ class DeleteAllocationTokensCommandTest extends CommandTestCase<DeleteAllocation
|
||||
othrNot = persistToken("asdgfho7HASDS", null, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_deleteOnlyUnredeemedTokensWithPrefix() throws Exception {
|
||||
runCommandForced("--prefix", "prefix");
|
||||
assertThat(reloadTokens(preNot1, preNot2)).isEmpty();
|
||||
assertNonexistent(preNot1, preNot2);
|
||||
assertThat(reloadTokens(preRed1, preRed2, othrRed, othrNot))
|
||||
.containsExactly(preRed1, preRed2, othrRed, othrNot);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_deleteSingleAllocationToken() throws Exception {
|
||||
runCommandForced("--prefix", "asdgfho7HASDS");
|
||||
assertThat(reloadTokens(othrNot)).isEmpty();
|
||||
assertNonexistent(othrNot);
|
||||
assertThat(reloadTokens(preRed1, preRed2, preNot1, preNot2, othrRed))
|
||||
.containsExactly(preRed1, preRed2, preNot1, preNot2, othrRed);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_deleteParticularTokens() throws Exception {
|
||||
runCommandForced("--tokens", "prefix2978204,asdgfho7HASDS");
|
||||
assertThat(reloadTokens(preNot1, othrNot)).isEmpty();
|
||||
assertNonexistent(preNot1, othrNot);
|
||||
assertThat(reloadTokens(preRed1, preRed2, preNot2, othrRed))
|
||||
.containsExactly(preRed1, preRed2, preNot2, othrRed);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_deleteTokensWithNonExistentPrefix_doesNothing() throws Exception {
|
||||
runCommandForced("--prefix", "nonexistent");
|
||||
assertThat(reloadTokens(preRed1, preRed2, preNot1, preNot2, othrRed, othrNot))
|
||||
.containsExactly(preRed1, preRed2, preNot1, preNot2, othrRed, othrNot);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_dryRun_deletesNothing() throws Exception {
|
||||
runCommandForced("--prefix", "prefix", "--dry_run");
|
||||
assertThat(reloadTokens(preRed1, preRed2, preNot1, preNot2, othrRed, othrNot))
|
||||
@@ -93,27 +97,27 @@ class DeleteAllocationTokensCommandTest extends CommandTestCase<DeleteAllocation
|
||||
assertInStdout("Would delete tokens: prefix2978204, prefix8ZZZhs8");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_defaultOptions_doesntDeletePerDomainTokens() throws Exception {
|
||||
AllocationToken preDom1 = persistToken("prefixasdfg897as", "foo.bar", false);
|
||||
AllocationToken preDom2 = persistToken("prefix98HAZXadbn", "foo.bar", true);
|
||||
runCommandForced("--prefix", "prefix");
|
||||
assertThat(reloadTokens(preNot1, preNot2)).isEmpty();
|
||||
assertNonexistent(preNot1, preNot2);
|
||||
assertThat(reloadTokens(preRed1, preRed2, preDom1, preDom2, othrRed, othrNot))
|
||||
.containsExactly(preRed1, preRed2, preDom1, preDom2, othrRed, othrNot);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_withDomains_doesDeletePerDomainTokens() throws Exception {
|
||||
AllocationToken preDom1 = persistToken("prefixasdfg897as", "foo.bar", false);
|
||||
AllocationToken preDom2 = persistToken("prefix98HAZXadbn", "foo.bar", true);
|
||||
runCommandForced("--prefix", "prefix", "--with_domains");
|
||||
assertThat(reloadTokens(preNot1, preNot2, preDom1)).isEmpty();
|
||||
assertNonexistent(preNot1, preNot2, preDom1);
|
||||
assertThat(reloadTokens(preRed1, preRed2, preDom2, othrRed, othrNot))
|
||||
.containsExactly(preRed1, preRed2, preDom2, othrRed, othrNot);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSkipUnlimitedUseTokens() throws Exception {
|
||||
AllocationToken unlimitedUseToken =
|
||||
persistResource(
|
||||
@@ -125,17 +129,18 @@ class DeleteAllocationTokensCommandTest extends CommandTestCase<DeleteAllocation
|
||||
assertThat(reloadTokens(unlimitedUseToken)).containsExactly(unlimitedUseToken);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_batching() throws Exception {
|
||||
for (int i = 0; i < 50; i++) {
|
||||
persistToken(String.format("batch%2d", i), null, i % 2 == 0);
|
||||
}
|
||||
assertThat(ofy().load().type(AllocationToken.class).count()).isEqualTo(56);
|
||||
assertThat(transactIfJpaTm(() -> tm().loadAll(AllocationToken.class).size())).isEqualTo(56);
|
||||
runCommandForced("--prefix", "batch");
|
||||
assertThat(ofy().load().type(AllocationToken.class).count()).isEqualTo(56 - 25);
|
||||
assertThat(transactIfJpaTm(() -> tm().loadAll(AllocationToken.class).size()))
|
||||
.isEqualTo(56 - 25);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void test_prefixIsRequired() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(IllegalArgumentException.class, this::runCommandForced);
|
||||
@@ -144,7 +149,7 @@ class DeleteAllocationTokensCommandTest extends CommandTestCase<DeleteAllocation
|
||||
.isEqualTo("Must provide one of --tokens or --prefix, not both / neither");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_bothPrefixAndTokens() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -155,7 +160,7 @@ class DeleteAllocationTokensCommandTest extends CommandTestCase<DeleteAllocation
|
||||
.isEqualTo("Must provide one of --tokens or --prefix, not both / neither");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_emptyPrefix() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(IllegalArgumentException.class, () -> runCommandForced("--prefix", ""));
|
||||
@@ -173,12 +178,16 @@ class DeleteAllocationTokensCommandTest extends CommandTestCase<DeleteAllocation
|
||||
String domainToPersist = domainName != null ? domainName : "example.foo";
|
||||
DomainBase domain = persistActiveDomain(domainToPersist);
|
||||
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1051L);
|
||||
builder.setRedemptionHistoryEntry(DomainHistoryVKey.create(historyEntryKey));
|
||||
builder.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey));
|
||||
}
|
||||
return persistResource(builder.build());
|
||||
}
|
||||
|
||||
private static Collection<AllocationToken> reloadTokens(AllocationToken... tokens) {
|
||||
return ofy().load().entities(tokens).values();
|
||||
private static ImmutableList<AllocationToken> reloadTokens(AllocationToken... tokens) {
|
||||
return transactIfJpaTm(() -> tm().loadAll(ImmutableSet.copyOf(tokens)));
|
||||
}
|
||||
|
||||
private static void assertNonexistent(AllocationToken... tokens) {
|
||||
Arrays.stream(tokens).forEach(t -> transactIfJpaTm(() -> assertThat(tm().exists(t)).isFalse()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ package google.registry.tools;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
import static google.registry.testing.DatabaseHelper.assertAllocationTokens;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
@@ -39,11 +40,14 @@ import com.google.common.collect.Iterables;
|
||||
import com.google.common.io.Files;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.persistence.DomainHistoryVKey;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.testing.DeterministicStringGenerator;
|
||||
import google.registry.testing.DeterministicStringGenerator.Rule;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeSleeper;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.util.StringGenerator.Alphabets;
|
||||
import java.io.File;
|
||||
@@ -51,10 +55,10 @@ import java.util.Collection;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
|
||||
/** Unit tests for {@link GenerateAllocationTokensCommand}. */
|
||||
@DualDatabaseTest
|
||||
class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAllocationTokensCommand> {
|
||||
|
||||
@BeforeEach
|
||||
@@ -64,14 +68,14 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
new Retrier(new FakeSleeper(new FakeClock(DateTime.parse("2000-01-01TZ"))), 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_oneToken() throws Exception {
|
||||
runCommand("--prefix", "blah", "--number", "1", "--length", "9");
|
||||
assertAllocationTokens(createToken("blah123456789", null, null));
|
||||
assertInStdout("blah123456789");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_threeTokens() throws Exception {
|
||||
runCommand("--prefix", "foo", "--number", "3", "--length", "10");
|
||||
assertAllocationTokens(
|
||||
@@ -81,14 +85,14 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
assertInStdout("foo123456789A\nfooBCDEFGHJKL\nfooMNPQRSTUVW");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_defaults() throws Exception {
|
||||
runCommand("--number", "1");
|
||||
assertAllocationTokens(createToken("123456789ABCDEFG", null, null));
|
||||
assertInStdout("123456789ABCDEFG");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_retry() throws Exception {
|
||||
command = spy(command);
|
||||
RemoteApiException fakeException = new RemoteApiException("foo", "foo", "foo", new Exception());
|
||||
@@ -103,7 +107,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
verify(command, times(3)).saveTokens(ArgumentMatchers.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_tokenCollision() throws Exception {
|
||||
AllocationToken existingToken =
|
||||
persistResource(
|
||||
@@ -116,24 +120,24 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
assertInStdout("DEADBEEFDEFGHJKLMNPQ");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_dryRun_outputsButDoesntSave() throws Exception {
|
||||
runCommand("--prefix", "foo", "--number", "2", "--length", "10", "--dry_run");
|
||||
assertAllocationTokens();
|
||||
assertInStdout("foo123456789A\nfooBCDEFGHJKL");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_largeNumberOfTokens() throws Exception {
|
||||
command.stringGenerator =
|
||||
new DeterministicStringGenerator(Alphabets.BASE_58, Rule.PREPEND_COUNTER);
|
||||
runCommand("--prefix", "ooo", "--number", "100", "--length", "16");
|
||||
// The deterministic string generator makes it too much hassle to assert about each token, so
|
||||
// just assert total number.
|
||||
assertThat(ofy().load().type(AllocationToken.class).count()).isEqualTo(100);
|
||||
assertThat(transactIfJpaTm(() -> tm().loadAll(AllocationToken.class).size())).isEqualTo(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_domainNames() throws Exception {
|
||||
createTld("tld");
|
||||
File domainNamesFile = tmpDir.resolve("domain_names.txt").toFile();
|
||||
@@ -147,7 +151,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
"foo1.tld,123456789ABCDEFG\nboo2.tld,HJKLMNPQRSTUVWXY\nbaz9.tld,Zabcdefghijkmnop");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_promotionToken() throws Exception {
|
||||
DateTime promoStart = DateTime.now(UTC);
|
||||
DateTime promoEnd = promoStart.plusMonths(1);
|
||||
@@ -181,24 +185,24 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_specifyTokens() throws Exception {
|
||||
runCommand("--tokens", "foobar,foobaz");
|
||||
assertAllocationTokens(createToken("foobar", null, null), createToken("foobaz", null, null));
|
||||
assertInStdout("foobar", "foobaz");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_specifyManyTokens() throws Exception {
|
||||
command.stringGenerator =
|
||||
new DeterministicStringGenerator(Alphabets.BASE_58, Rule.PREPEND_COUNTER);
|
||||
Collection<String> sampleTokens = command.stringGenerator.createStrings(13, 100);
|
||||
runCommand("--tokens", Joiner.on(",").join(sampleTokens));
|
||||
assertInStdout(Iterables.toArray(sampleTokens, String.class));
|
||||
assertThat(ofy().load().type(AllocationToken.class).count()).isEqualTo(100);
|
||||
assertThat(transactIfJpaTm(() -> tm().loadAll(AllocationToken.class).size())).isEqualTo(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_mustSpecifyNumberOfTokensOrDomainsFile() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(IllegalArgumentException.class, () -> runCommand("--prefix", "FEET"));
|
||||
@@ -207,7 +211,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
.isEqualTo("Must specify exactly one of '--number', '--domain_names_file', and '--tokens'");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_mustNotSpecifyBothNumberOfTokensAndDomainsFile() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -222,7 +226,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
.isEqualTo("Must specify exactly one of '--number', '--domain_names_file', and '--tokens'");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_mustNotSpecifyBothNumberOfTokensAndTokenStrings() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -237,7 +241,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
.isEqualTo("Must specify exactly one of '--number', '--domain_names_file', and '--tokens'");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_mustNotSpecifyBothTokenStringsAndDomainsFile() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -252,7 +256,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
.isEqualTo("Must specify exactly one of '--number', '--domain_names_file', and '--tokens'");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_specifiesAlreadyExistingToken() throws Exception {
|
||||
runCommand("--tokens", "foobar");
|
||||
beforeEachCommandTestCase(); // reset the command variables
|
||||
@@ -263,7 +267,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
.isEqualTo("Cannot create specified tokens; the following tokens already exist: [foobar]");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_invalidTokenType() {
|
||||
ParameterException thrown =
|
||||
assertThrows(
|
||||
@@ -274,7 +278,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
.isEqualTo("Invalid value for -t parameter. Allowed values:[SINGLE_USE, UNLIMITED_USE]");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_invalidTokenStatusTransition() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
@@ -289,7 +293,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
.isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_lengthOfZero() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -301,7 +305,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
"Token length should not be 0. To generate exact tokens, use the --tokens parameter.");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_unlimitedUseMustHaveTransitions() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
@@ -313,7 +317,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
|
||||
private AllocationToken createToken(
|
||||
String token,
|
||||
@Nullable DomainHistoryVKey redemptionHistoryEntry,
|
||||
@Nullable VKey<? extends HistoryEntry> redemptionHistoryEntry,
|
||||
@Nullable String domainName) {
|
||||
AllocationToken.Builder builder =
|
||||
new AllocationToken.Builder().setToken(token).setTokenType(SINGLE_USE);
|
||||
|
||||
@@ -28,7 +28,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.persistence.DomainHistoryVKey;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -85,7 +85,7 @@ class GetAllocationTokenCommandTest extends CommandTestCase<GetAllocationTokenCo
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDomainName("fqqdn.tld")
|
||||
.setRedemptionHistoryEntry(
|
||||
DomainHistoryVKey.create(Key.create(createHistoryEntryForEppResource(domain))))
|
||||
HistoryEntry.createVKey(Key.create(createHistoryEntryForEppResource(domain))))
|
||||
.build());
|
||||
runCommand("foo");
|
||||
assertInStdout(
|
||||
|
||||
@@ -30,12 +30,14 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@DualDatabaseTest
|
||||
class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocationTokensCommand> {
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testUpdateTlds_setTlds() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(builderWithPromo().setAllowedTlds(ImmutableSet.of("toRemove")).build());
|
||||
@@ -43,7 +45,7 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
assertThat(reloadResource(token).getAllowedTlds()).containsExactly("tld", "example");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testUpdateTlds_clearTlds() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(builderWithPromo().setAllowedTlds(ImmutableSet.of("toRemove")).build());
|
||||
@@ -51,7 +53,7 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
assertThat(reloadResource(token).getAllowedTlds()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testUpdateClientIds_setClientIds() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
@@ -61,7 +63,7 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
.containsExactly("clientone", "clienttwo");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testUpdateClientIds_clearClientIds() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
@@ -70,14 +72,14 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
assertThat(reloadResource(token).getAllowedRegistrarIds()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testUpdateDiscountFraction() throws Exception {
|
||||
AllocationToken token = persistResource(builderWithPromo().setDiscountFraction(0.5).build());
|
||||
runCommandForced("--prefix", "token", "--discount_fraction", "0.15");
|
||||
assertThat(reloadResource(token).getDiscountFraction()).isEqualTo(0.15);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testUpdateDiscountPremiums() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
@@ -88,14 +90,14 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
assertThat(reloadResource(token).shouldDiscountPremiums()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testUpdateDiscountYears() throws Exception {
|
||||
AllocationToken token = persistResource(builderWithPromo().setDiscountFraction(0.5).build());
|
||||
runCommandForced("--prefix", "token", "--discount_years", "4");
|
||||
assertThat(reloadResource(token).getDiscountYears()).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testUpdateStatusTransitions() throws Exception {
|
||||
DateTime now = DateTime.now(UTC);
|
||||
AllocationToken token = persistResource(builderWithPromo().build());
|
||||
@@ -110,7 +112,7 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
.containsExactly(START_OF_TIME, NOT_STARTED, now.minusDays(1), VALID, now, CANCELLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testUpdateStatusTransitions_badTransitions() {
|
||||
DateTime now = DateTime.now(UTC);
|
||||
persistResource(builderWithPromo().build());
|
||||
@@ -130,7 +132,7 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
.isEqualTo("tokenStatusTransitions map cannot transition from NOT_STARTED to ENDED.");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testUpdate_onlyWithPrefix() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(builderWithPromo().setAllowedTlds(ImmutableSet.of("tld")).build());
|
||||
@@ -146,7 +148,7 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
assertThat(reloadResource(otherToken).getAllowedTlds()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testUpdate_onlyTokensProvided() throws Exception {
|
||||
AllocationToken firstToken =
|
||||
persistResource(builderWithPromo().setAllowedTlds(ImmutableSet.of("tld")).build());
|
||||
@@ -170,7 +172,7 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
assertThat(reloadResource(thirdToken).getAllowedTlds()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testDoNothing() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
@@ -186,7 +188,7 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
assertThat(reloaded.getDiscountFraction()).isEqualTo(token.getDiscountFraction());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_bothTokensAndPrefix() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
@@ -196,7 +198,7 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
.isEqualTo("Must provide one of --tokens or --prefix, not both / neither");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_neitherTokensNorPrefix() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
@@ -205,7 +207,7 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
.isEqualTo("Must provide one of --tokens or --prefix, not both / neither");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_emptyPrefix() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(IllegalArgumentException.class, () -> runCommandForced("--prefix", ""));
|
||||
|
||||
@@ -278,8 +278,8 @@ class google.registry.model.domain.DomainHistory {
|
||||
}
|
||||
class google.registry.model.domain.GracePeriod {
|
||||
google.registry.model.domain.rgp.GracePeriodStatus type;
|
||||
google.registry.persistence.VKey<google.registry.model.billing.BillingEvent$OneTime> billingEventOneTime;
|
||||
google.registry.persistence.VKey<google.registry.model.billing.BillingEvent$Recurring> billingEventRecurring;
|
||||
google.registry.persistence.BillingVKey$BillingEventVKey billingEventOneTime;
|
||||
google.registry.persistence.BillingVKey$BillingRecurrenceVKey billingEventRecurring;
|
||||
java.lang.Long gracePeriodId;
|
||||
java.lang.String clientId;
|
||||
org.joda.time.DateTime expirationTime;
|
||||
@@ -888,10 +888,17 @@ enum google.registry.model.transfer.TransferStatus {
|
||||
SERVER_APPROVED;
|
||||
SERVER_CANCELLED;
|
||||
}
|
||||
class google.registry.persistence.DomainHistoryVKey {
|
||||
com.googlecode.objectify.Key<T> ofyKey;
|
||||
java.lang.Class<? extends T> kind;
|
||||
java.lang.Long domainHistoryId;
|
||||
java.lang.Object sqlKey;
|
||||
java.lang.String domainRepoId;
|
||||
class google.registry.persistence.BillingVKey$BillingEventVKey {
|
||||
java.lang.Long billingId;
|
||||
java.lang.Long historyRevisionId;
|
||||
java.lang.String repoId;
|
||||
}
|
||||
class google.registry.persistence.BillingVKey$BillingRecurrenceVKey {
|
||||
java.lang.Long billingId;
|
||||
java.lang.Long historyRevisionId;
|
||||
java.lang.String repoId;
|
||||
}
|
||||
class google.registry.persistence.DomainHistoryVKey {
|
||||
java.lang.Long historyRevisionId;
|
||||
java.lang.String repoId;
|
||||
}
|
||||
|
||||
@@ -261,11 +261,11 @@ td.section {
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">generated on</td>
|
||||
<td class="property_value">2020-12-05 00:29:03.934355</td>
|
||||
<td class="property_value">2020-12-21 17:41:59.495189</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">last flyway file</td>
|
||||
<td id="lastFlywayFile" class="property_value">V81__drop_spec11_fkeys.sql</td>
|
||||
<td id="lastFlywayFile" class="property_value">V83__add_indexes_on_domainhost.sql</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -284,7 +284,7 @@ td.section {
|
||||
generated on
|
||||
</text>
|
||||
<text text-anchor="start" x="4027.94" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
2020-12-05 00:29:03.934355
|
||||
2020-12-21 17:41:59.495189
|
||||
</text>
|
||||
<polygon fill="none" stroke="#888888" points="3940.44,-4 3940.44,-44 4205.44,-44 4205.44,-4 3940.44,-4" /> <!-- allocationtoken_a08ccbef -->
|
||||
<g id="node1" class="node">
|
||||
@@ -568,14 +568,14 @@ td.section {
|
||||
</g> <!-- billingevent_a57d1815->domainhistory_a54cc226 -->
|
||||
<g id="edge29" class="edge">
|
||||
<title>billingevent_a57d1815:w->domainhistory_a54cc226:e</title>
|
||||
<path fill="none" stroke="black" d="M3219.23,-1185.48C3119.47,-1183.67 2900.01,-1171.05 2844,-1231.68 2807.56,-1271.12 2864.68,-1677.42 2826,-1714.68 2771.83,-1766.87 2203.88,-1761.13 2149,-1709.68 2124.77,-1686.96 2153.47,-1433.39 2130.83,-1389.98" />
|
||||
<path fill="none" stroke="black" d="M3219.23,-1185.48C3119.47,-1183.66 2899.97,-1171.01 2844,-1231.68 2806.23,-1272.62 2866.09,-1694 2826,-1732.68 2771.86,-1784.9 2203.77,-1779.24 2149,-1727.68 2123.44,-1703.61 2155.05,-1434.99 2131.08,-1390.04" />
|
||||
<polygon fill="black" stroke="black" points="3227.5,-1185.57 3237.45,-1190.18 3232.5,-1185.62 3237.5,-1185.68 3237.5,-1185.68 3237.5,-1185.68 3232.5,-1185.62 3237.55,-1181.18 3227.5,-1185.57 3227.5,-1185.57" />
|
||||
<ellipse fill="none" stroke="black" cx="3223.5" cy="-1185.53" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="2120.64,-1388.2 2126.91,-1380.41 2128.47,-1381.66 2122.2,-1389.45 2120.64,-1388.2" />
|
||||
<polyline fill="none" stroke="black" points="2123,-1383.68 2126.89,-1386.81 " />
|
||||
<polygon fill="black" stroke="black" points="2124.54,-1391.33 2130.81,-1383.54 2132.37,-1384.8 2126.1,-1392.59 2124.54,-1391.33" />
|
||||
<polyline fill="none" stroke="black" points="2126.89,-1386.81 2130.79,-1389.95 " />
|
||||
<text text-anchor="start" x="2555" y="-1755.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<polygon fill="black" stroke="black" points="2120.69,-1388.22 2126.88,-1380.37 2128.45,-1381.6 2122.26,-1389.46 2120.69,-1388.22" />
|
||||
<polyline fill="none" stroke="black" points="2123,-1383.68 2126.93,-1386.77 " />
|
||||
<polygon fill="black" stroke="black" points="2124.62,-1391.32 2130.81,-1383.46 2132.38,-1384.7 2126.19,-1392.55 2124.62,-1391.32" />
|
||||
<polyline fill="none" stroke="black" points="2126.93,-1386.77 2130.86,-1389.86 " />
|
||||
<text text-anchor="start" x="2555" y="-1773.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_billing_event_domain_history
|
||||
</text>
|
||||
</g> <!-- registrar_6e1503e3 -->
|
||||
@@ -617,14 +617,14 @@ td.section {
|
||||
</g> <!-- billingevent_a57d1815->registrar_6e1503e3 -->
|
||||
<g id="edge49" class="edge">
|
||||
<title>billingevent_a57d1815:w->registrar_6e1503e3:e</title>
|
||||
<path fill="none" stroke="black" d="M3222.22,-1215.14C3145.17,-1290.97 2857.5,-1753.9 2826,-1770.68 2621.48,-1879.58 2536.71,-1789.68 2305,-1789.68 2305,-1789.68 2305,-1789.68 640.5,-1789.68 391.14,-1789.68 324.15,-1646.84 217,-1421.68 199.61,-1385.13 230.41,-733.26 198.48,-654.45" />
|
||||
<polygon fill="black" stroke="black" points="3229.25,-1210.33 3240.04,-1208.39 3233.37,-1207.5 3237.5,-1204.68 3237.5,-1204.68 3237.5,-1204.68 3233.37,-1207.5 3234.96,-1200.96 3229.25,-1210.33 3229.25,-1210.33" />
|
||||
<ellipse fill="none" stroke="black" cx="3225.95" cy="-1212.59" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="188.45,-650.76 195.89,-644.08 197.22,-645.57 189.78,-652.25 188.45,-650.76" />
|
||||
<polyline fill="none" stroke="black" points="191.5,-646.68 194.84,-650.4 " />
|
||||
<polygon fill="black" stroke="black" points="191.79,-654.48 199.23,-647.8 200.57,-649.29 193.12,-655.97 191.79,-654.48" />
|
||||
<polyline fill="none" stroke="black" points="194.84,-650.4 198.18,-654.12 " />
|
||||
<text text-anchor="start" x="1546" y="-1793.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<path fill="none" stroke="black" d="M3222.55,-1214.98C3145.79,-1291.68 2858.1,-1771.28 2826,-1788.68 2622.29,-1899.09 2536.71,-1807.68 2305,-1807.68 2305,-1807.68 2305,-1807.68 640.5,-1807.68 385.83,-1807.68 323.7,-1652.92 217,-1421.68 200.04,-1384.93 230.46,-733.23 198.49,-654.45" />
|
||||
<polygon fill="black" stroke="black" points="3229.27,-1210.35 3240.05,-1208.38 3233.38,-1207.51 3237.5,-1204.68 3237.5,-1204.68 3237.5,-1204.68 3233.38,-1207.51 3234.95,-1200.97 3229.27,-1210.35 3229.27,-1210.35" />
|
||||
<ellipse fill="none" stroke="black" cx="3225.97" cy="-1212.62" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="188.45,-650.76 195.89,-644.08 197.22,-645.56 189.79,-652.25 188.45,-650.76" />
|
||||
<polyline fill="none" stroke="black" points="191.5,-646.68 194.84,-650.39 " />
|
||||
<polygon fill="black" stroke="black" points="191.79,-654.48 199.23,-647.8 200.57,-649.28 193.13,-655.97 191.79,-654.48" />
|
||||
<polyline fill="none" stroke="black" points="194.84,-650.39 198.18,-654.11 " />
|
||||
<text text-anchor="start" x="1546" y="-1811.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_billing_event_registrar_id
|
||||
</text>
|
||||
</g> <!-- billingcancellation_6eedf614 -->
|
||||
@@ -1754,14 +1754,14 @@ td.section {
|
||||
</g> <!-- pollmessage_614a523e->domainhistory_a54cc226 -->
|
||||
<g id="edge41" class="edge">
|
||||
<title>pollmessage_614a523e:w->domainhistory_a54cc226:e</title>
|
||||
<path fill="none" stroke="black" d="M3236.68,-451.33C3193.59,-492.92 3251.04,-717.2 3211,-769.68 3103.7,-910.31 2943.71,-777.57 2844,-923.68 2820.69,-957.83 2855.47,-1638.68 2826,-1667.68 2799.19,-1694.06 2176.77,-1696.04 2149,-1670.68 2104.49,-1630.02 2177.5,-1411.56 2132.78,-1386.1" />
|
||||
<path fill="none" stroke="black" d="M3236.68,-451.33C3193.59,-492.92 3251.04,-717.2 3211,-769.68 3103.7,-910.31 2943.67,-777.54 2844,-923.68 2820.11,-958.71 2856.25,-1656.97 2826,-1686.68 2799.16,-1713.03 2176.7,-1714.12 2149,-1688.68 2126.25,-1667.78 2151.43,-1433.27 2130.77,-1390.39" />
|
||||
<polygon fill="black" stroke="black" points="3244.2,-448.35 3255.15,-448.86 3248.85,-446.51 3253.5,-444.68 3253.5,-444.68 3253.5,-444.68 3248.85,-446.51 3251.85,-440.49 3244.2,-448.35 3244.2,-448.35" />
|
||||
<ellipse fill="none" stroke="black" cx="3240.48" cy="-449.82" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="2122.77,-1388.77 2125.17,-1379.06 2127.11,-1379.54 2124.71,-1389.25 2122.77,-1388.77" />
|
||||
<polyline fill="none" stroke="black" points="2123,-1383.68 2127.85,-1384.88 " />
|
||||
<polygon fill="black" stroke="black" points="2127.62,-1389.97 2130.03,-1380.27 2131.97,-1380.75 2129.56,-1390.45 2127.62,-1389.97" />
|
||||
<polyline fill="none" stroke="black" points="2127.85,-1384.88 2132.71,-1386.08 " />
|
||||
<text text-anchor="start" x="2550.5" y="-1692.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<polygon fill="black" stroke="black" points="2120.49,-1388.11 2127.03,-1380.55 2128.54,-1381.85 2122,-1389.42 2120.49,-1388.11" />
|
||||
<polyline fill="none" stroke="black" points="2123,-1383.68 2126.78,-1386.94 " />
|
||||
<polygon fill="black" stroke="black" points="2124.27,-1391.38 2130.81,-1383.81 2132.32,-1385.12 2125.79,-1392.69 2124.27,-1391.38" />
|
||||
<polyline fill="none" stroke="black" points="2126.78,-1386.94 2130.57,-1390.21 " />
|
||||
<text text-anchor="start" x="2550.5" y="-1711.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_poll_message_domain_history
|
||||
</text>
|
||||
</g> <!-- host_f21b78de -->
|
||||
@@ -2252,21 +2252,29 @@ td.section {
|
||||
</g> <!-- domainhistoryhost_9f3f23ee -->
|
||||
<g id="node18" class="node">
|
||||
<title>domainhistoryhost_9f3f23ee</title>
|
||||
<polygon fill="#ebcef2" stroke="transparent" points="2502.5,-1634.68 2502.5,-1653.68 2726.5,-1653.68 2726.5,-1634.68 2502.5,-1634.68" />
|
||||
<text text-anchor="start" x="2504.5" y="-1641.48" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">
|
||||
<polygon fill="#ebcef2" stroke="transparent" points="2502.5,-1653.68 2502.5,-1672.68 2726.5,-1672.68 2726.5,-1653.68 2502.5,-1653.68" />
|
||||
<text text-anchor="start" x="2504.5" y="-1660.48" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">
|
||||
public.DomainHistoryHost
|
||||
</text>
|
||||
<polygon fill="#ebcef2" stroke="transparent" points="2726.5,-1634.68 2726.5,-1653.68 2800.5,-1653.68 2800.5,-1634.68 2726.5,-1634.68" />
|
||||
<text text-anchor="start" x="2761.5" y="-1640.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<polygon fill="#ebcef2" stroke="transparent" points="2726.5,-1653.68 2726.5,-1672.68 2800.5,-1672.68 2800.5,-1653.68 2726.5,-1653.68" />
|
||||
<text text-anchor="start" x="2761.5" y="-1659.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
[table]
|
||||
</text>
|
||||
<text text-anchor="start" x="2504.5" y="-1621.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<text text-anchor="start" x="2504.5" y="-1640.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
domain_history_history_revision_id
|
||||
</text>
|
||||
<text text-anchor="start" x="2720.5" y="-1640.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
</text>
|
||||
<text text-anchor="start" x="2728.5" y="-1640.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
int8 not null
|
||||
</text>
|
||||
<text text-anchor="start" x="2504.5" y="-1621.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
host_repo_id
|
||||
</text>
|
||||
<text text-anchor="start" x="2720.5" y="-1621.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
</text>
|
||||
<text text-anchor="start" x="2728.5" y="-1621.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
int8 not null
|
||||
text
|
||||
</text>
|
||||
<text text-anchor="start" x="2504.5" y="-1602.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
domain_history_domain_repo_id
|
||||
@@ -2276,31 +2284,31 @@ td.section {
|
||||
<text text-anchor="start" x="2728.5" y="-1602.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
text not null
|
||||
</text>
|
||||
<polygon fill="none" stroke="#888888" points="2501.5,-1596.18 2501.5,-1655.18 2801.5,-1655.18 2801.5,-1596.18 2501.5,-1596.18" />
|
||||
<polygon fill="none" stroke="#888888" points="2501.5,-1595.68 2501.5,-1673.68 2801.5,-1673.68 2801.5,-1595.68 2501.5,-1595.68" />
|
||||
</g> <!-- domainhistoryhost_9f3f23ee->domainhistory_a54cc226 -->
|
||||
<g id="edge34" class="edge">
|
||||
<title>domainhistoryhost_9f3f23ee:w->domainhistory_a54cc226:e</title>
|
||||
<path fill="none" stroke="black" d="M2484.5,-1599.62C2477.03,-1593.04 2472.75,-1582.78 2459,-1577.68 2393.88,-1553.5 2197.51,-1587.39 2149,-1537.68 2112.74,-1500.51 2169.71,-1316.37 2133.01,-1290.66" />
|
||||
<polygon fill="black" stroke="black" points="2492.08,-1602.32 2499.99,-1609.91 2496.79,-1604 2501.5,-1605.68 2501.5,-1605.68 2501.5,-1605.68 2496.79,-1604 2503.01,-1601.44 2492.08,-1602.32 2492.08,-1602.32" />
|
||||
<ellipse fill="none" stroke="black" cx="2488.31" cy="-1600.98" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="2122.53,-1292.75 2125.39,-1283.17 2127.3,-1283.74 2124.45,-1293.32 2122.53,-1292.75" />
|
||||
<polyline fill="none" stroke="black" points="2123,-1287.68 2127.79,-1289.1 " />
|
||||
<polygon fill="black" stroke="black" points="2127.32,-1294.18 2130.18,-1284.6 2132.09,-1285.17 2129.24,-1294.75 2127.32,-1294.18" />
|
||||
<polyline fill="none" stroke="black" points="2127.79,-1289.1 2132.58,-1290.53 " />
|
||||
<text text-anchor="start" x="2209.5" y="-1581.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<path fill="none" stroke="black" d="M2483.6,-1602.41C2476.45,-1599.52 2470.57,-1595.37 2459,-1592.68 2391.03,-1576.88 2196.88,-1593.43 2149,-1542.68 2112.51,-1503.99 2171.14,-1315.28 2132.74,-1290.39" />
|
||||
<polygon fill="black" stroke="black" points="2491.66,-1603.88 2500.69,-1610.1 2496.58,-1604.78 2501.5,-1605.68 2501.5,-1605.68 2501.5,-1605.68 2496.58,-1604.78 2502.31,-1601.25 2491.66,-1603.88 2491.66,-1603.88" />
|
||||
<ellipse fill="none" stroke="black" cx="2487.73" cy="-1603.16" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="2122.62,-1292.76 2125.31,-1283.13 2127.23,-1283.66 2124.55,-1293.3 2122.62,-1292.76" />
|
||||
<polyline fill="none" stroke="black" points="2123,-1287.68 2127.82,-1289.02 " />
|
||||
<polygon fill="black" stroke="black" points="2127.44,-1294.1 2130.12,-1284.47 2132.05,-1285.01 2129.36,-1294.64 2127.44,-1294.1" />
|
||||
<polyline fill="none" stroke="black" points="2127.82,-1289.02 2132.63,-1290.36 " />
|
||||
<text text-anchor="start" x="2209.5" y="-1596.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fka9woh3hu8gx5x0vly6bai327n
|
||||
</text>
|
||||
</g> <!-- domainhistoryhost_9f3f23ee->domainhistory_a54cc226 -->
|
||||
<g id="edge35" class="edge">
|
||||
<title>domainhistoryhost_9f3f23ee:w->domainhistory_a54cc226:e</title>
|
||||
<path fill="none" stroke="black" d="M2483.4,-1625.01C2337.73,-1630.44 2257.65,-1697.78 2149,-1592.68 2118.12,-1562.81 2161.46,-1411.66 2132.58,-1387.06" />
|
||||
<polygon fill="black" stroke="black" points="2491.5,-1624.86 2501.58,-1629.18 2496.5,-1624.77 2501.5,-1624.68 2501.5,-1624.68 2501.5,-1624.68 2496.5,-1624.77 2501.42,-1620.18 2491.5,-1624.86 2491.5,-1624.86" />
|
||||
<ellipse fill="none" stroke="black" cx="2487.5" cy="-1624.94" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="2122.28,-1388.72 2125.61,-1379.3 2127.49,-1379.96 2124.16,-1389.39 2122.28,-1388.72" />
|
||||
<polyline fill="none" stroke="black" points="2123,-1383.68 2127.71,-1385.34 " />
|
||||
<polygon fill="black" stroke="black" points="2126.99,-1390.39 2130.32,-1380.96 2132.21,-1381.63 2128.88,-1391.06 2126.99,-1390.39" />
|
||||
<polyline fill="none" stroke="black" points="2127.71,-1385.34 2132.43,-1387.01 " />
|
||||
<text text-anchor="start" x="2209.5" y="-1655.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<path fill="none" stroke="black" d="M2483.37,-1645C2337.45,-1650.16 2256.62,-1714.12 2149,-1607.68 2116.16,-1575.19 2164.36,-1412.23 2132.83,-1386.97" />
|
||||
<polygon fill="black" stroke="black" points="2491.5,-1644.85 2501.58,-1649.18 2496.5,-1644.76 2501.5,-1644.68 2501.5,-1644.68 2501.5,-1644.68 2496.5,-1644.76 2501.42,-1640.18 2491.5,-1644.85 2491.5,-1644.85" />
|
||||
<ellipse fill="none" stroke="black" cx="2487.5" cy="-1644.93" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="2122.36,-1388.73 2125.54,-1379.25 2127.43,-1379.89 2124.26,-1389.37 2122.36,-1388.73" />
|
||||
<polyline fill="none" stroke="black" points="2123,-1383.68 2127.74,-1385.26 " />
|
||||
<polygon fill="black" stroke="black" points="2127.1,-1390.32 2130.28,-1380.84 2132.17,-1381.48 2129,-1390.96 2127.1,-1390.32" />
|
||||
<polyline fill="none" stroke="black" points="2127.74,-1385.26 2132.48,-1386.85 " />
|
||||
<text text-anchor="start" x="2209.5" y="-1673.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fka9woh3hu8gx5x0vly6bai327n
|
||||
</text>
|
||||
</g> <!-- domaintransactionrecord_6e77ff61 -->
|
||||
@@ -2430,14 +2438,14 @@ td.section {
|
||||
</g> <!-- graceperiodhistory_40ccc1f1->domainhistory_a54cc226 -->
|
||||
<g id="edge38" class="edge">
|
||||
<title>graceperiodhistory_40ccc1f1:w->domainhistory_a54cc226:e</title>
|
||||
<path fill="none" stroke="black" d="M2481.03,-1520.46C2406.48,-1519.12 2174.32,-1511.17 2149,-1486.68 2119.7,-1458.33 2159.5,-1315.54 2132.5,-1291.2" />
|
||||
<polygon fill="black" stroke="black" points="2489.5,-1520.56 2499.45,-1525.18 2494.5,-1520.62 2499.5,-1520.68 2499.5,-1520.68 2499.5,-1520.68 2494.5,-1520.62 2499.55,-1516.18 2489.5,-1520.56 2489.5,-1520.56" />
|
||||
<ellipse fill="none" stroke="black" cx="2485.5" cy="-1520.51" rx="4" ry="4" />
|
||||
<path fill="none" stroke="black" d="M2481.33,-1520.94C2392.06,-1523.15 2201.35,-1537.31 2149,-1486.68 2119.7,-1458.33 2159.5,-1315.54 2132.5,-1291.2" />
|
||||
<polygon fill="black" stroke="black" points="2489.5,-1520.82 2499.57,-1525.18 2494.5,-1520.75 2499.5,-1520.68 2499.5,-1520.68 2499.5,-1520.68 2494.5,-1520.75 2499.43,-1516.18 2489.5,-1520.82 2489.5,-1520.82" />
|
||||
<ellipse fill="none" stroke="black" cx="2485.5" cy="-1520.88" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="2122.2,-1292.71 2125.68,-1283.34 2127.55,-1284.03 2124.07,-1293.41 2122.2,-1292.71" />
|
||||
<polyline fill="none" stroke="black" points="2123,-1287.68 2127.69,-1289.42 " />
|
||||
<polygon fill="black" stroke="black" points="2126.88,-1294.45 2130.37,-1285.08 2132.24,-1285.77 2128.76,-1295.15 2126.88,-1294.45" />
|
||||
<polyline fill="none" stroke="black" points="2127.69,-1289.42 2132.37,-1291.16 " />
|
||||
<text text-anchor="start" x="2210" y="-1524.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<text text-anchor="start" x="2210" y="-1528.48" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk7w3cx8d55q8bln80e716tr7b8
|
||||
</text>
|
||||
</g> <!-- graceperiodhistory_40ccc1f1->domainhistory_a54cc226 -->
|
||||
@@ -4790,6 +4798,11 @@ td.section {
|
||||
<td class="minwidth">domain_history_history_revision_id</td>
|
||||
<td class="minwidth">int8 not null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"></td>
|
||||
<td class="minwidth">host_repo_id</td>
|
||||
<td class="minwidth">text</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"></td>
|
||||
<td class="minwidth">domain_history_domain_repo_id</td>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -79,3 +79,5 @@ V78__add_history_id_for_redemption_history_entry.sql
|
||||
V79__drop_foreign_keys_on_pollmessage.sql
|
||||
V80__defer_bill_event_key.sql
|
||||
V81__drop_spec11_fkeys.sql
|
||||
V82__add_columns_to_restore_symmetric_billing_vkey.sql
|
||||
V83__add_indexes_on_domainhost.sql
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
-- 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.
|
||||
|
||||
alter table "GracePeriod"
|
||||
add column if not exists "billing_event_domain_repo_id" text;
|
||||
alter table "GracePeriod"
|
||||
add column if not exists "billing_recurrence_domain_repo_id" text;
|
||||
alter table "GracePeriodHistory"
|
||||
add column if not exists "billing_event_domain_repo_id" text;
|
||||
alter table "GracePeriodHistory"
|
||||
add column if not exists "billing_recurrence_domain_repo_id" text;
|
||||
@@ -0,0 +1,23 @@
|
||||
-- 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.
|
||||
|
||||
alter table if exists "DomainHistoryHost"
|
||||
add constraint UKt2e7ae3t8gcsxd13wjx2ka7ij unique (
|
||||
domain_history_history_revision_id,
|
||||
domain_history_domain_repo_id,
|
||||
host_repo_id);
|
||||
|
||||
alter table if exists "DomainHost"
|
||||
add constraint UKat9erbh52e4lg3jw6ai9wkjj9 unique (
|
||||
domain_repo_id, host_repo_id);
|
||||
@@ -398,8 +398,10 @@
|
||||
grace_period_id int8 not null,
|
||||
billing_event_id int8,
|
||||
billing_event_history_id int8,
|
||||
billing_event_domain_repo_id text,
|
||||
billing_recurrence_id int8,
|
||||
billing_recurrence_history_id int8,
|
||||
billing_recurrence_domain_repo_id text,
|
||||
registrar_id text not null,
|
||||
domain_repo_id text not null,
|
||||
expiration_time timestamptz not null,
|
||||
@@ -411,8 +413,10 @@
|
||||
grace_period_history_revision_id int8 not null,
|
||||
billing_event_id int8,
|
||||
billing_event_history_id int8,
|
||||
billing_event_domain_repo_id text,
|
||||
billing_recurrence_id int8,
|
||||
billing_recurrence_history_id int8,
|
||||
billing_recurrence_domain_repo_id text,
|
||||
registrar_id text not null,
|
||||
domain_repo_id text not null,
|
||||
expiration_time timestamptz not null,
|
||||
@@ -764,6 +768,12 @@ create index IDXrh4xmrot9bd63o382ow9ltfig on "DomainHistory" (creation_time);
|
||||
create index IDXaro1omfuaxjwmotk3vo00trwm on "DomainHistory" (history_registrar_id);
|
||||
create index IDXsu1nam10cjes9keobapn5jvxj on "DomainHistory" (history_type);
|
||||
create index IDX6w3qbtgce93cal2orjg1tw7b7 on "DomainHistory" (history_modification_time);
|
||||
|
||||
alter table if exists "DomainHistoryHost"
|
||||
add constraint UKt2e7ae3t8gcsxd13wjx2ka7ij unique (domain_history_history_revision_id, domain_history_domain_repo_id, host_repo_id);
|
||||
|
||||
alter table if exists "DomainHost"
|
||||
add constraint UKat9erbh52e4lg3jw6ai9wkjj9 unique (domain_repo_id, host_repo_id);
|
||||
create index IDXj1mtx98ndgbtb1bkekahms18w on "GracePeriod" (domain_repo_id);
|
||||
create index IDXd01j17vrpjxaerxdmn8bwxs7s on "GracePeriodHistory" (domain_repo_id);
|
||||
create index IDXfg2nnjlujxo6cb9fha971bq2n on "HostHistory" (creation_time);
|
||||
|
||||
@@ -533,7 +533,9 @@ CREATE TABLE public."GracePeriod" (
|
||||
expiration_time timestamp with time zone NOT NULL,
|
||||
type text NOT NULL,
|
||||
billing_event_history_id bigint,
|
||||
billing_recurrence_history_id bigint
|
||||
billing_recurrence_history_id bigint,
|
||||
billing_event_domain_repo_id text,
|
||||
billing_recurrence_domain_repo_id text
|
||||
);
|
||||
|
||||
|
||||
@@ -552,7 +554,9 @@ CREATE TABLE public."GracePeriodHistory" (
|
||||
expiration_time timestamp with time zone NOT NULL,
|
||||
type text NOT NULL,
|
||||
domain_history_revision_id bigint,
|
||||
grace_period_id bigint NOT NULL
|
||||
grace_period_id bigint NOT NULL,
|
||||
billing_event_domain_repo_id text,
|
||||
billing_recurrence_domain_repo_id text
|
||||
);
|
||||
|
||||
|
||||
@@ -1424,6 +1428,22 @@ ALTER TABLE ONLY public."RegistryLock"
|
||||
ADD CONSTRAINT idx_registry_lock_repo_id_revision_id UNIQUE (repo_id, revision_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: DomainHost ukat9erbh52e4lg3jw6ai9wkjj9; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public."DomainHost"
|
||||
ADD CONSTRAINT ukat9erbh52e4lg3jw6ai9wkjj9 UNIQUE (domain_repo_id, host_repo_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: DomainHistoryHost ukt2e7ae3t8gcsxd13wjx2ka7ij; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public."DomainHistoryHost"
|
||||
ADD CONSTRAINT ukt2e7ae3t8gcsxd13wjx2ka7ij UNIQUE (domain_history_history_revision_id, domain_history_domain_repo_id, host_repo_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: allocation_token_domain_name_idx; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
@@ -74,10 +74,12 @@ test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
// Sets up integration test with a registry environment. The target environment is
|
||||
// passed by the 'test.gcp_integration.env' property. Test runner must have been
|
||||
// authorized to access the corresponding GCP project, e.g., by running 'gcloud auth'
|
||||
// or placing a credential file at a well known place.
|
||||
// Sets up integration test with a registry environment. The target environment
|
||||
// is passed by the 'test.gcp_integration.env' property. Test runner must have
|
||||
// been authorized to access the corresponding GCP project, e.g., by running
|
||||
// 'gcloud auth application-default login' or by downloading a credential file
|
||||
// and assign the path to it to the GOOGLE_APPLICATION_CREDENTIALS environment
|
||||
// variable.
|
||||
//
|
||||
// A typical use case is to run tests from desktop that accesses Cloud resources. See
|
||||
// core/src/test/java/google/registry/beam/initsql/BeamJpaModuleTest.java for an example.
|
||||
|
||||
Reference in New Issue
Block a user