mirror of
https://github.com/google/nomulus
synced 2026-05-19 06:11:49 +00:00
Compare commits
11 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7c2e8fba5 | ||
|
|
a299df3005 | ||
|
|
a9b35c163d | ||
|
|
9da24d114c | ||
|
|
7dd5876315 | ||
|
|
d1a259f63a | ||
|
|
8c5d2e9d92 | ||
|
|
cca1306b09 | ||
|
|
47071b0fbb | ||
|
|
d83565d37e | ||
|
|
a557b3f376 |
@@ -207,6 +207,9 @@
|
||||
{
|
||||
"moduleLicense": "GNU Library General Public License v2.1 or later"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "GNU Lesser General Public License v3.0"
|
||||
},
|
||||
// This is just 3-clause BSD.
|
||||
{
|
||||
"moduleLicense": "Go License"
|
||||
|
||||
@@ -91,7 +91,6 @@ public class ResaveAllEppResourcesPipeline implements Serializable {
|
||||
}
|
||||
|
||||
void setupPipeline(Pipeline pipeline) {
|
||||
options.setIsolationOverride(TransactionIsolationLevel.TRANSACTION_READ_COMMITTED);
|
||||
if (options.getFast()) {
|
||||
fastResaveContacts(pipeline);
|
||||
fastResaveDomains(pipeline);
|
||||
@@ -194,6 +193,7 @@ public class ResaveAllEppResourcesPipeline implements Serializable {
|
||||
PipelineOptionsFactory.fromArgs(args)
|
||||
.withValidation()
|
||||
.as(ResaveAllEppResourcesPipelineOptions.class);
|
||||
options.setIsolationOverride(TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ);
|
||||
new ResaveAllEppResourcesPipeline(options).run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ public abstract class ThreatMatch implements Serializable {
|
||||
|
||||
private static final String THREAT_TYPE_FIELD = "threatType";
|
||||
private static final String DOMAIN_NAME_FIELD = "domainName";
|
||||
private static final String OUTDATED_NAME_FIELD = "fullyQualifiedDomainName";
|
||||
|
||||
/** Returns what kind of threat it is (malware, phishing etc.) */
|
||||
public abstract String threatType();
|
||||
@@ -46,7 +47,12 @@ public abstract class ThreatMatch implements Serializable {
|
||||
|
||||
/** Parses a {@link JSONObject} and returns an equivalent {@link ThreatMatch}. */
|
||||
public static ThreatMatch fromJSON(JSONObject threatMatch) throws JSONException {
|
||||
// TODO: delete OUTDATED_NAME_FIELD once we no longer process reports saved with
|
||||
// fullyQualifiedDomainName in them, likely 2023
|
||||
return new AutoValue_ThreatMatch(
|
||||
threatMatch.getString(THREAT_TYPE_FIELD), threatMatch.getString(DOMAIN_NAME_FIELD));
|
||||
threatMatch.getString(THREAT_TYPE_FIELD),
|
||||
threatMatch.has(OUTDATED_NAME_FIELD)
|
||||
? threatMatch.getString(OUTDATED_NAME_FIELD)
|
||||
: threatMatch.getString(DOMAIN_NAME_FIELD));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<!-- TODO(b/249863289): disable until it is safe to run this pipeline
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResourcesPipeline?fast=true]]></url>
|
||||
<description>
|
||||
@@ -110,6 +111,7 @@
|
||||
<schedule>1st monday of month 09:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
-->
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/updateRegistrarRdapBaseUrls]]></url>
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<!-- TODO(b/249863289): disable until it is safe to run this pipeline
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResourcesPipeline?fast=true]]></url>
|
||||
<description>
|
||||
@@ -94,6 +95,7 @@
|
||||
<schedule>1st monday of month 09:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
-->
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportDomainLists&runInEmpty]]></url>
|
||||
|
||||
@@ -87,7 +87,6 @@ import google.registry.model.poll.PendingActionNotificationResponse.DomainPendin
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Registry;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -182,10 +181,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
DomainHistory domainHistory =
|
||||
historyBuilder.setType(DOMAIN_UPDATE).setDomain(newDomain).build();
|
||||
validateNewState(newDomain);
|
||||
if (!Objects.equals(newDomain.getDsData(), existingDomain.getDsData())
|
||||
|| !Objects.equals(newDomain.getNsHosts(), existingDomain.getNsHosts())) {
|
||||
dnsQueue.addDomainRefreshTask(targetId);
|
||||
}
|
||||
dnsQueue.addDomainRefreshTask(targetId);
|
||||
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||
entitiesToSave.add(newDomain, domainHistory);
|
||||
Optional<BillingEvent.OneTime> statusUpdateBillingEvent =
|
||||
@@ -271,18 +267,12 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
.setLastEppUpdateRegistrarId(registrarId)
|
||||
.addStatusValues(add.getStatusValues())
|
||||
.removeStatusValues(remove.getStatusValues())
|
||||
.addNameservers(add.getNameservers().stream().collect(toImmutableSet()))
|
||||
.removeNameservers(remove.getNameservers().stream().collect(toImmutableSet()))
|
||||
.removeContacts(remove.getContacts())
|
||||
.addContacts(add.getContacts())
|
||||
.setRegistrant(firstNonNull(change.getRegistrant(), domain.getRegistrant()))
|
||||
.setAuthInfo(firstNonNull(change.getAuthInfo(), domain.getAuthInfo()));
|
||||
|
||||
if (!add.getNameservers().isEmpty()) {
|
||||
domainBuilder.addNameservers(add.getNameservers().stream().collect(toImmutableSet()));
|
||||
}
|
||||
if (!remove.getNameservers().isEmpty()) {
|
||||
domainBuilder.removeNameservers(remove.getNameservers().stream().collect(toImmutableSet()));
|
||||
}
|
||||
|
||||
Optional<DomainUpdateSuperuserExtension> superuserExt =
|
||||
eppInput.getSingleExtension(DomainUpdateSuperuserExtension.class);
|
||||
if (superuserExt.isPresent()) {
|
||||
|
||||
@@ -38,7 +38,7 @@ import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.transfer.TransferData.TransferServerApproveEntity;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithLongVKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import google.registry.persistence.converter.JodaMoneyType;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -295,7 +295,7 @@ public abstract class BillingEvent extends ImmutableObject
|
||||
@Index(columnList = "cancellation_matching_billing_recurrence_id")
|
||||
})
|
||||
@AttributeOverride(name = "id", column = @Column(name = "billing_event_id"))
|
||||
@WithLongVKey(compositeKey = true)
|
||||
@WithVKey(Long.class)
|
||||
public static class OneTime extends BillingEvent {
|
||||
|
||||
/** The billable value. */
|
||||
@@ -473,7 +473,7 @@ public abstract class BillingEvent extends ImmutableObject
|
||||
@Index(columnList = "recurrence_time_of_year")
|
||||
})
|
||||
@AttributeOverride(name = "id", column = @Column(name = "billing_recurrence_id"))
|
||||
@WithLongVKey(compositeKey = true)
|
||||
@WithVKey(Long.class)
|
||||
public static class Recurring extends BillingEvent {
|
||||
|
||||
/**
|
||||
@@ -606,7 +606,7 @@ public abstract class BillingEvent extends ImmutableObject
|
||||
@Index(columnList = "billing_recurrence_id")
|
||||
})
|
||||
@AttributeOverride(name = "id", column = @Column(name = "billing_cancellation_id"))
|
||||
@WithLongVKey(compositeKey = true)
|
||||
@WithVKey(Long.class)
|
||||
public static class Cancellation extends BillingEvent {
|
||||
|
||||
/** The billing time of the charge that is being cancelled. */
|
||||
|
||||
@@ -17,7 +17,7 @@ package google.registry.model.bulkquery;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithStringVKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.Entity;
|
||||
@@ -31,7 +31,7 @@ import javax.persistence.Entity;
|
||||
* <p>Please refer to {@link BulkQueryEntities} for more information.
|
||||
*/
|
||||
@Entity(name = "Domain")
|
||||
@WithStringVKey
|
||||
@WithVKey(String.class)
|
||||
@Access(AccessType.FIELD)
|
||||
public class DomainLite extends DomainBase {
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import google.registry.model.EppResource.ForeignKeyedEppResource;
|
||||
import google.registry.model.annotations.ExternalMessagingName;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithStringVKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.Entity;
|
||||
@@ -45,7 +45,7 @@ import org.joda.time.DateTime;
|
||||
@Index(columnList = "searchName")
|
||||
})
|
||||
@ExternalMessagingName("contact")
|
||||
@WithStringVKey(compositeKey = true)
|
||||
@WithVKey(String.class)
|
||||
@Access(AccessType.FIELD)
|
||||
public class Contact extends ContactBase implements ForeignKeyedEppResource {
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.model.domain.secdns.DomainDsData;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithStringVKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
@@ -66,7 +66,7 @@ import org.joda.time.DateTime;
|
||||
@Index(columnList = "transfer_billing_event_id"),
|
||||
@Index(columnList = "transfer_billing_recurrence_id")
|
||||
})
|
||||
@WithStringVKey(compositeKey = true)
|
||||
@WithVKey(String.class)
|
||||
@ExternalMessagingName("domain")
|
||||
@Access(AccessType.FIELD)
|
||||
public class Domain extends DomainBase implements ForeignKeyedEppResource {
|
||||
|
||||
@@ -41,7 +41,7 @@ import google.registry.model.common.TimedTransitionProperty;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.DomainHistoryVKey;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithStringVKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -58,7 +58,7 @@ import org.joda.time.DateTime;
|
||||
|
||||
/** An entity representing an allocation token. */
|
||||
@Entity
|
||||
@WithStringVKey(compositeKey = true)
|
||||
@WithVKey(String.class)
|
||||
@Table(
|
||||
indexes = {
|
||||
@Index(columnList = "token", name = "allocation_token_token_idx", unique = true),
|
||||
@@ -300,6 +300,9 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
|
||||
!getInstance().tokenType.equals(TokenType.PACKAGE)
|
||||
|| getInstance().renewalPriceBehavior.equals(RenewalPriceBehavior.SPECIFIED),
|
||||
"Package tokens must have renewalPriceBehavior set to SPECIFIED");
|
||||
checkArgument(
|
||||
!getInstance().tokenType.equals(TokenType.PACKAGE) || !getInstance().discountPremiums,
|
||||
"Package tokens cannot discount premium names");
|
||||
checkArgument(
|
||||
getInstance().domainName == null || TokenType.SINGLE_USE.equals(getInstance().tokenType),
|
||||
"Domain name can only be specified for SINGLE_USE tokens");
|
||||
|
||||
@@ -19,7 +19,7 @@ import google.registry.model.EppResource.ForeignKeyedEppResource;
|
||||
import google.registry.model.annotations.ExternalMessagingName;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithStringVKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
|
||||
@@ -51,7 +51,7 @@ import javax.persistence.AccessType;
|
||||
@javax.persistence.Index(columnList = "currentSponsorRegistrarId")
|
||||
})
|
||||
@ExternalMessagingName("host")
|
||||
@WithStringVKey(compositeKey = true)
|
||||
@WithVKey(String.class)
|
||||
@Access(AccessType.FIELD) // otherwise it'll use the default if the repoId (property)
|
||||
public class Host extends HostBase implements ForeignKeyedEppResource {
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ import google.registry.model.transfer.TransferResponse;
|
||||
import google.registry.model.transfer.TransferResponse.ContactTransferResponse;
|
||||
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithLongVKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import google.registry.util.NullIgnoringCollectionBuilder;
|
||||
import java.util.Optional;
|
||||
import javax.persistence.AttributeOverride;
|
||||
@@ -342,7 +342,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||
*/
|
||||
@Entity
|
||||
@DiscriminatorValue("ONE_TIME")
|
||||
@WithLongVKey(compositeKey = true)
|
||||
@WithVKey(Long.class)
|
||||
public static class OneTime extends PollMessage {
|
||||
|
||||
@Embedded
|
||||
@@ -544,7 +544,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||
*/
|
||||
@Entity
|
||||
@DiscriminatorValue("AUTORENEW")
|
||||
@WithLongVKey(compositeKey = true)
|
||||
@WithVKey(Long.class)
|
||||
public static class Autorenew extends PollMessage {
|
||||
|
||||
/** The target id of the autorenew event. */
|
||||
|
||||
@@ -278,7 +278,7 @@ public abstract class PersistenceModule {
|
||||
replicaInstanceConnectionName.ifPresent(
|
||||
name -> overrides.put(HIKARI_DS_CLOUD_SQL_INSTANCE, name));
|
||||
overrides.put(
|
||||
Environment.ISOLATION, TransactionIsolationLevel.TRANSACTION_READ_COMMITTED.name());
|
||||
Environment.ISOLATION, TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ.name());
|
||||
return new JpaTransactionManagerImpl(create(overrides), clock);
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ public abstract class PersistenceModule {
|
||||
replicaInstanceConnectionName.ifPresent(
|
||||
name -> overrides.put(HIKARI_DS_CLOUD_SQL_INSTANCE, name));
|
||||
overrides.put(
|
||||
Environment.ISOLATION, TransactionIsolationLevel.TRANSACTION_READ_COMMITTED.name());
|
||||
Environment.ISOLATION, TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ.name());
|
||||
return new JpaTransactionManagerImpl(create(overrides), clock);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Entity;
|
||||
|
||||
/**
|
||||
* Annotation for {@link Entity} which id is string type and needs an {@link AttributeConverter} for
|
||||
* its VKey.
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface WithStringVKey {
|
||||
/**
|
||||
* Sets the suffix of the class name for the {@link AttributeConverter} generated by
|
||||
* StringVKeyProcessor. If not set, the suffix will be the type name of the VKey. Note that the
|
||||
* class name will be "VKeyConverter_" concatenated with the suffix.
|
||||
*/
|
||||
String classNameSuffix() default "";
|
||||
|
||||
/**
|
||||
* Set to true if this is a composite vkey.
|
||||
*
|
||||
* <p>For composite VKeys, we don't attempt to define an objectify key when loading from SQL: the
|
||||
* enclosing class has to take care of that.
|
||||
*/
|
||||
boolean compositeKey() default false;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -14,29 +14,22 @@
|
||||
|
||||
package google.registry.persistence;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Entity;
|
||||
|
||||
/**
|
||||
* Annotation for {@link Entity} which id is long type and needs an {@link AttributeConverter} for
|
||||
* its VKey.
|
||||
* Annotation for {@link Entity} that can be saved as a foreign key in the form of a {@link VKey} in
|
||||
* another table.
|
||||
*
|
||||
* <p>A {@link AttributeConverter} named {@code VKeyConverter_[EntityClassSimpleName]} will be
|
||||
* automatically generated by {@code google.registry.processors.VKeyProcessor}, this class must be
|
||||
* manually added to {@code persistence.xml} in order for it to be picked up by Hibernate.
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface WithLongVKey {
|
||||
/**
|
||||
* Sets the suffix of the class name for the {@link AttributeConverter} generated by
|
||||
* LongVKeyProcessor. If not set, the suffix will be the type name of the VKey. Note that the
|
||||
* class name will be "VKeyConverter_" concatenated with the suffix.
|
||||
*/
|
||||
String classNameSuffix() default "";
|
||||
|
||||
/**
|
||||
* Set to true if this is a composite vkey.
|
||||
*
|
||||
* <p>For composite VKeys, we don't attempt to define an objectify key when loading from SQL: the
|
||||
* enclosing class has to take care of that.
|
||||
*/
|
||||
boolean compositeKey() default false;
|
||||
public @interface WithVKey {
|
||||
/** The type of the SQL primary ID of the entity that is saved in the {@link VKey} */
|
||||
Class<? extends Serializable> value();
|
||||
}
|
||||
@@ -14,20 +14,34 @@
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.io.Serializable;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.AttributeConverter;
|
||||
|
||||
/** Converts VKey to a string or long column. */
|
||||
/**
|
||||
* Converts {@link VKey} to/from a type that can be directly stored in the database.
|
||||
*
|
||||
* <p>Typically the converted type is {@link String} or {@link Long}.
|
||||
*/
|
||||
public abstract class VKeyConverter<T, C extends Serializable>
|
||||
implements AttributeConverter<VKey<? extends T>, C> {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
public C convertToDatabaseColumn(@Nullable VKey<? extends T> attribute) {
|
||||
return attribute == null ? null : (C) attribute.getSqlKey();
|
||||
if (attribute == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getKeyClass().cast(attribute.getSqlKey());
|
||||
} catch (ClassCastException e) {
|
||||
throw new RuntimeException(
|
||||
String.format(
|
||||
"Cannot cast SQL key %s of type %s to type %s",
|
||||
attribute.getSqlKey(), attribute.getSqlKey().getClass(), getKeyClass()),
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -36,27 +50,12 @@ public abstract class VKeyConverter<T, C extends Serializable>
|
||||
if (dbData == null) {
|
||||
return null;
|
||||
}
|
||||
Class<T> clazz = getAttributeClass();
|
||||
Key<T> ofyKey;
|
||||
if (!hasCompositeOfyKey()) {
|
||||
// If this isn't a composite key, we can create the Ofy key from the SQL key.
|
||||
ofyKey =
|
||||
dbData instanceof String
|
||||
? Key.create(clazz, (String) dbData)
|
||||
: Key.create(clazz, (Long) dbData);
|
||||
return VKey.create(clazz, dbData, ofyKey);
|
||||
} else {
|
||||
// We don't know how to create the Ofy key and probably don't have everything necessary to do
|
||||
// it anyway, so just create an asymmetric key - the containing object will have to convert it
|
||||
// into a symmetric key.
|
||||
return VKey.createSql(clazz, dbData);
|
||||
}
|
||||
return VKey.createSql(getEntityClass(), dbData);
|
||||
}
|
||||
|
||||
protected boolean hasCompositeOfyKey() {
|
||||
return false;
|
||||
}
|
||||
/** Returns the class of the entity that the VKey represents. */
|
||||
protected abstract Class<T> getEntityClass();
|
||||
|
||||
/** Returns the class of the attribute. */
|
||||
protected abstract Class<T> getAttributeClass();
|
||||
/** Returns the class of the key that the VKey holds. */
|
||||
protected abstract Class<C> getKeyClass();
|
||||
}
|
||||
|
||||
@@ -15,30 +15,31 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.util.Clock;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Abstract command to print one or more resources to stdout. */
|
||||
@Parameters(separators = " =")
|
||||
abstract class GetEppResourceCommand implements CommandWithRemoteApi {
|
||||
|
||||
private final DateTime now = DateTime.now(UTC);
|
||||
|
||||
@Parameter(
|
||||
names = "--read_timestamp",
|
||||
description = "Timestamp to use when reading. May not be in the past.")
|
||||
protected DateTime readTimestamp = now;
|
||||
protected DateTime readTimestamp;
|
||||
|
||||
@Parameter(
|
||||
names = "--expand",
|
||||
description = "Fully expand the requested resource. NOTE: Output may be lengthy.")
|
||||
boolean expand;
|
||||
|
||||
@Inject Clock clock;
|
||||
|
||||
/** Runs the command's own logic that calls {@link #printResource}. */
|
||||
abstract void runAndPrint();
|
||||
|
||||
@@ -59,7 +60,11 @@ abstract class GetEppResourceCommand implements CommandWithRemoteApi {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
checkArgument(!readTimestamp.isBefore(now), "--read_timestamp may not be in the past");
|
||||
if (readTimestamp == null) {
|
||||
readTimestamp = clock.nowUtc();
|
||||
}
|
||||
checkArgument(
|
||||
!readTimestamp.isBefore(clock.nowUtc()), "--read_timestamp may not be in the past");
|
||||
runAndPrint();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import static google.registry.model.EppResourceUtils.checkResourcesExist;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
@@ -38,11 +37,13 @@ import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.tools.soy.DomainRenewSoyInfo;
|
||||
import google.registry.tools.soy.UniformRapidSuspensionSoyInfo;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
@@ -119,10 +120,12 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
|
||||
/** Set of status values to remove. */
|
||||
ImmutableSet<String> removeStatuses;
|
||||
|
||||
@Inject Clock clock;
|
||||
|
||||
@Override
|
||||
protected void initMutatingEppToolCommand() {
|
||||
superuser = true;
|
||||
DateTime now = DateTime.now(UTC);
|
||||
DateTime now = clock.nowUtc();
|
||||
ImmutableList<String> newCanonicalHosts =
|
||||
newHosts.stream().map(DomainNameUtils::canonicalizeHostname).collect(toImmutableList());
|
||||
ImmutableSet<String> newHostsSet = ImmutableSet.copyOf(newCanonicalHosts);
|
||||
|
||||
@@ -61,21 +61,14 @@ public class CreateSyntheticDomainHistoriesPipeline implements Serializable {
|
||||
"Create synthetic domain histories to fix RDE for b/248112997";
|
||||
private static final DateTime BAD_PIPELINE_START_TIME =
|
||||
DateTime.parse("2022-09-05T09:00:00.000Z");
|
||||
private static final DateTime BAD_PIPELINE_END_TIME = DateTime.parse("2022-09-10T12:00:00.000Z");
|
||||
|
||||
static void setup(Pipeline pipeline, String registryAdminRegistrarId) {
|
||||
pipeline
|
||||
.apply(
|
||||
"Read all domain repo IDs",
|
||||
RegistryJpaIO.read(
|
||||
"SELECT d.repoId FROM Domain d WHERE deletionTime > :badPipelineStartTime AND NOT"
|
||||
+ " EXISTS (SELECT 1 FROM DomainHistory dh WHERE dh.domainRepoId = d.repoId"
|
||||
+ " AND dh.modificationTime > :badPipelineEndTime)",
|
||||
ImmutableMap.of(
|
||||
"badPipelineStartTime",
|
||||
BAD_PIPELINE_START_TIME,
|
||||
"badPipelineEndTime",
|
||||
BAD_PIPELINE_END_TIME),
|
||||
"SELECT d.repoId FROM Domain d WHERE deletionTime > :badPipelineStartTime",
|
||||
ImmutableMap.of("badPipelineStartTime", BAD_PIPELINE_START_TIME),
|
||||
String.class,
|
||||
repoId -> VKey.createSql(Domain.class, repoId)))
|
||||
.apply(
|
||||
@@ -117,7 +110,7 @@ public class CreateSyntheticDomainHistoriesPipeline implements Serializable {
|
||||
RegistryPipelineOptions options =
|
||||
PipelineOptionsFactory.fromArgs(args).withValidation().as(RegistryPipelineOptions.class);
|
||||
RegistryPipelineOptions.validateRegistryPipelineOptions(options);
|
||||
options.setIsolationOverride(TransactionIsolationLevel.TRANSACTION_READ_COMMITTED);
|
||||
options.setIsolationOverride(TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ);
|
||||
String registryAdminRegistrarId =
|
||||
DaggerCreateSyntheticDomainHistoriesPipeline_ConfigComponent.create()
|
||||
.getRegistryAdminRegistrarId();
|
||||
|
||||
@@ -47,7 +47,6 @@ import static google.registry.testing.DomainSubject.assertAboutDomains;
|
||||
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
|
||||
import static google.registry.testing.HistoryEntrySubject.assertAboutHistoryEntries;
|
||||
import static google.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
|
||||
import static google.registry.testing.TaskQueueHelper.assertNoDnsTasksEnqueued;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
@@ -497,11 +496,13 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
expectedDsData.stream()
|
||||
.map(ds -> ds.cloneWithDomainRepoId(resource.getRepoId()))
|
||||
.collect(toImmutableSet()));
|
||||
if (dnsTaskEnqueued) {
|
||||
assertDnsTasksEnqueued("example.tld");
|
||||
} else {
|
||||
assertNoDnsTasksEnqueued();
|
||||
}
|
||||
|
||||
// TODO: REENABLE AFTER PROPER FIX FOR DNS PUBLISHING TASKS IS FOUND
|
||||
// if (dnsTaskEnqueued) {
|
||||
// assertDnsTasksEnqueued("example.tld");
|
||||
// } else {
|
||||
// assertNoDnsTasksEnqueued();
|
||||
// }
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1746,9 +1747,4 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
assertAboutDomains().that(reloadResourceByForeignKey()).hasNoAutorenewEndTime();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDnsTaskIsNotTriggeredWhenNoDSChangeSubmitted() {
|
||||
setEppInput("domain_update_no_ds_change.xml");
|
||||
assertNoDnsTasksEnqueued();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,6 +228,18 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
.isEqualTo("Package tokens must have renewalPriceBehavior set to SPECIFIED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFail_packageTokenDiscountPremium() {
|
||||
AllocationToken.Builder builder =
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setDiscountPremiums(true);
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Package tokens cannot discount premium names");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBuild_DomainNameWithLessThanTwoParts() {
|
||||
IllegalArgumentException thrown =
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithLongVKey;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Test SQL persistence of VKey. */
|
||||
public class LongVKeyConverterTest {
|
||||
|
||||
@RegisterExtension
|
||||
public final AppEngineExtension appEngineExtension =
|
||||
new AppEngineExtension.Builder()
|
||||
.withCloudSql()
|
||||
.withoutCannedData()
|
||||
.withJpaUnitTestEntities(
|
||||
TestLongEntity.class,
|
||||
VKeyConverter_LongType.class,
|
||||
VKeyConverter_CompositeLongType.class)
|
||||
.withOfyTestEntities(TestLongEntity.class, CompositeKeyTestLongEntity.class)
|
||||
.build();
|
||||
|
||||
@Test
|
||||
void testRoundTrip() {
|
||||
TestLongEntity original =
|
||||
new TestLongEntity(
|
||||
VKey.createSql(TestLongEntity.class, 10L),
|
||||
VKey.createSql(CompositeKeyTestLongEntity.class, 20L));
|
||||
insertInDb(original);
|
||||
|
||||
TestLongEntity retrieved =
|
||||
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestLongEntity.class, "id"));
|
||||
assertThat(retrieved.number.getSqlKey()).isEqualTo(10L);
|
||||
assertThat(retrieved.number.getOfyKey().getId()).isEqualTo(10L);
|
||||
|
||||
assertThat(retrieved.composite.getSqlKey()).isEqualTo(20L);
|
||||
assertThat(retrieved.composite.maybeGetOfyKey().isPresent()).isFalse();
|
||||
}
|
||||
|
||||
@Entity(name = "TestLongEntity")
|
||||
@com.googlecode.objectify.annotation.Entity
|
||||
@WithLongVKey(classNameSuffix = "LongType")
|
||||
static class TestLongEntity extends ImmutableObject {
|
||||
@com.googlecode.objectify.annotation.Id @Id String id = "id";
|
||||
|
||||
VKey<TestLongEntity> number;
|
||||
VKey<CompositeKeyTestLongEntity> composite;
|
||||
|
||||
TestLongEntity(VKey<TestLongEntity> number, VKey<CompositeKeyTestLongEntity> composite) {
|
||||
this.number = number;
|
||||
this.composite = composite;
|
||||
}
|
||||
|
||||
/** Default constructor, needed for hibernate. */
|
||||
public TestLongEntity() {}
|
||||
}
|
||||
|
||||
@Entity(name = "CompositeKeyTestLongEntity")
|
||||
@com.googlecode.objectify.annotation.Entity
|
||||
@WithLongVKey(classNameSuffix = "CompositeLongType", compositeKey = true)
|
||||
static class CompositeKeyTestLongEntity {
|
||||
@com.googlecode.objectify.annotation.Id @Id String id = "id";
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithStringVKey;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Test SQL persistence of VKey. */
|
||||
public class StringVKeyConverterTest {
|
||||
|
||||
@RegisterExtension
|
||||
public final AppEngineExtension appEngineExtension =
|
||||
new AppEngineExtension.Builder()
|
||||
.withCloudSql()
|
||||
.withoutCannedData()
|
||||
.withJpaUnitTestEntities(
|
||||
TestStringEntity.class,
|
||||
VKeyConverter_StringType.class,
|
||||
VKeyConverter_CompositeStringType.class)
|
||||
.withOfyTestEntities(TestStringEntity.class, CompositeKeyTestStringEntity.class)
|
||||
.build();
|
||||
|
||||
@Test
|
||||
void testRoundTrip() {
|
||||
TestStringEntity original =
|
||||
new TestStringEntity(
|
||||
"TheRealSpartacus",
|
||||
VKey.createSql(TestStringEntity.class, "ImSpartacus!"),
|
||||
VKey.createSql(CompositeKeyTestStringEntity.class, "NoImSpartacus!"));
|
||||
insertInDb(original);
|
||||
|
||||
TestStringEntity retrieved =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> jpaTm().getEntityManager().find(TestStringEntity.class, "TheRealSpartacus"));
|
||||
assertThat(retrieved.other.getSqlKey()).isEqualTo("ImSpartacus!");
|
||||
assertThat(retrieved.other.getOfyKey().getName()).isEqualTo("ImSpartacus!");
|
||||
|
||||
assertThat(retrieved.composite.getSqlKey()).isEqualTo("NoImSpartacus!");
|
||||
assertThat(retrieved.composite.maybeGetOfyKey().isPresent()).isFalse();
|
||||
}
|
||||
|
||||
@Entity(name = "TestStringEntity")
|
||||
@com.googlecode.objectify.annotation.Entity
|
||||
@WithStringVKey(classNameSuffix = "StringType")
|
||||
static class TestStringEntity extends ImmutableObject {
|
||||
@com.googlecode.objectify.annotation.Id @Id String id;
|
||||
|
||||
VKey<TestStringEntity> other;
|
||||
VKey<CompositeKeyTestStringEntity> composite;
|
||||
|
||||
TestStringEntity(
|
||||
String id, VKey<TestStringEntity> other, VKey<CompositeKeyTestStringEntity> composite) {
|
||||
this.id = id;
|
||||
this.other = other;
|
||||
this.composite = composite;
|
||||
}
|
||||
|
||||
/** Default constructor, needed for hibernate. */
|
||||
public TestStringEntity() {}
|
||||
}
|
||||
|
||||
@Entity(name = "CompositeKeyTestStringEntity")
|
||||
@com.googlecode.objectify.annotation.Entity
|
||||
@WithStringVKey(classNameSuffix = "CompositeStringType", compositeKey = true)
|
||||
static class CompositeKeyTestStringEntity {
|
||||
@com.googlecode.objectify.annotation.Id @Id String id = "id";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Test SQL persistence of {@link VKey}. */
|
||||
public class VKeyConverterTest {
|
||||
|
||||
@RegisterExtension
|
||||
public final JpaUnitTestExtension jpa =
|
||||
new JpaTestExtensions.Builder()
|
||||
.withoutCannedData()
|
||||
.withEntityClass(
|
||||
TestEntity.class,
|
||||
TestStringEntity.class,
|
||||
TestLongEntity.class,
|
||||
VKeyConverter_TestStringEntity.class,
|
||||
VKeyConverter_TestLongEntity.class)
|
||||
.buildUnitTestExtension();
|
||||
|
||||
@Test
|
||||
void testRoundTrip() {
|
||||
TestStringEntity stringEntity = new TestStringEntity("TheRealSpartacus");
|
||||
VKey<TestStringEntity> stringKey = VKey.createSql(TestStringEntity.class, "TheRealSpartacus");
|
||||
TestLongEntity longEntity = new TestLongEntity(300L);
|
||||
VKey<TestLongEntity> longKey = VKey.createSql(TestLongEntity.class, 300L);
|
||||
TestEntity original = new TestEntity(1984L, stringKey, longKey);
|
||||
insertInDb(stringEntity, longEntity, original);
|
||||
|
||||
TestEntity retrieved =
|
||||
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, 1984L));
|
||||
assertThat(retrieved.stringKey).isEqualTo(stringKey);
|
||||
assertThat(retrieved.longKey).isEqualTo(longKey);
|
||||
}
|
||||
|
||||
@Entity(name = "TestStringEntity")
|
||||
@WithVKey(String.class)
|
||||
protected static class TestStringEntity extends ImmutableObject {
|
||||
@Id String id;
|
||||
|
||||
TestStringEntity(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/** Default constructor, needed for hibernate. */
|
||||
public TestStringEntity() {}
|
||||
}
|
||||
|
||||
@Entity(name = "TestLongEntity")
|
||||
@WithVKey(Long.class)
|
||||
protected static class TestLongEntity extends ImmutableObject {
|
||||
@Id Long id;
|
||||
|
||||
TestLongEntity(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/** Default constructor, needed for hibernate. */
|
||||
public TestLongEntity() {}
|
||||
}
|
||||
|
||||
@Entity(name = "TestEntity")
|
||||
@WithVKey(String.class)
|
||||
protected static class TestEntity extends ImmutableObject {
|
||||
@Id Long id;
|
||||
VKey<TestStringEntity> stringKey;
|
||||
VKey<TestLongEntity> longKey;
|
||||
|
||||
TestEntity(Long id, VKey<TestStringEntity> stringKey, VKey<TestLongEntity> longKey) {
|
||||
this.id = id;
|
||||
this.stringKey = stringKey;
|
||||
this.longKey = longKey;
|
||||
}
|
||||
|
||||
/** Default constructor, needed for hibernate. */
|
||||
public TestEntity() {}
|
||||
}
|
||||
}
|
||||
@@ -100,6 +100,24 @@ public class Spec11RegistrarThreatMatchesParserTest {
|
||||
assertThat(objectWithExtraFields).isEqualTo(objectWithoutExtraFields);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_worksWithOutdatedField() throws Exception {
|
||||
ThreatMatch objectWithOutdatedField =
|
||||
ThreatMatch.fromJSON(
|
||||
new JSONObject(
|
||||
ImmutableMap.of(
|
||||
"threatType", "MALWARE",
|
||||
"fullyQualifiedDomainName", "c.com")));
|
||||
ThreatMatch objectWithoutOutdatedFields =
|
||||
ThreatMatch.fromJSON(
|
||||
new JSONObject(
|
||||
ImmutableMap.of(
|
||||
"threatType", "MALWARE",
|
||||
"domainName", "c.com")));
|
||||
|
||||
assertThat(objectWithOutdatedField).isEqualTo(objectWithoutOutdatedFields);
|
||||
}
|
||||
|
||||
/** The expected contents of the sample spec11 report file */
|
||||
public static ImmutableSet<RegistrarThreatMatches> sampleThreatMatches() throws Exception {
|
||||
return ImmutableSet.of(getMatchA(), getMatchB());
|
||||
|
||||
@@ -19,7 +19,6 @@ import static com.google.common.collect.Iterables.toArray;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.beust.jcommander.JCommander;
|
||||
import com.google.common.base.Joiner;
|
||||
@@ -63,7 +62,7 @@ public abstract class CommandTestCase<C extends Command> {
|
||||
|
||||
protected C command;
|
||||
|
||||
protected final FakeClock fakeClock = new FakeClock(DateTime.now(UTC));
|
||||
protected final FakeClock fakeClock = new FakeClock(DateTime.parse("2022-09-01T00:00:00.000Z"));
|
||||
|
||||
@RegisterExtension
|
||||
public final AppEngineExtension appEngine =
|
||||
|
||||
@@ -19,22 +19,19 @@ import static google.registry.testing.DatabaseHelper.newContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistDeletedContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.beust.jcommander.ParameterException;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link GetContactCommand}. */
|
||||
class GetContactCommandTest extends CommandTestCase<GetContactCommand> {
|
||||
|
||||
private DateTime now = DateTime.now(UTC);
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
command.clock = fakeClock;
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -67,7 +64,7 @@ class GetContactCommandTest extends CommandTestCase<GetContactCommand> {
|
||||
|
||||
@Test
|
||||
void testSuccess_deletedContact() throws Exception {
|
||||
persistDeletedContact("sh8013", now.minusDays(1));
|
||||
persistDeletedContact("sh8013", fakeClock.nowUtc().minusDays(1));
|
||||
runCommand("sh8013");
|
||||
assertInStdout("Contact 'sh8013' does not exist or is deleted");
|
||||
}
|
||||
@@ -85,8 +82,9 @@ class GetContactCommandTest extends CommandTestCase<GetContactCommand> {
|
||||
|
||||
@Test
|
||||
void testSuccess_contactDeletedInFuture() throws Exception {
|
||||
persistResource(newContact("sh8013").asBuilder().setDeletionTime(now.plusDays(1)).build());
|
||||
runCommand("sh8013", "--read_timestamp=" + now.plusMonths(1));
|
||||
persistResource(
|
||||
newContact("sh8013").asBuilder().setDeletionTime(fakeClock.nowUtc().plusDays(1)).build());
|
||||
runCommand("sh8013", "--read_timestamp=" + fakeClock.nowUtc().plusMonths(1));
|
||||
assertInStdout("Contact 'sh8013' does not exist or is deleted");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ class GetDomainCommandTest extends CommandTestCase<GetDomainCommand> {
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
command.clock = fakeClock;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -19,22 +19,19 @@ import static google.registry.testing.DatabaseHelper.newHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistDeletedHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.beust.jcommander.ParameterException;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link GetHostCommand}. */
|
||||
class GetHostCommandTest extends CommandTestCase<GetHostCommand> {
|
||||
|
||||
private DateTime now = DateTime.now(UTC);
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
command.clock = fakeClock;
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -77,7 +74,7 @@ class GetHostCommandTest extends CommandTestCase<GetHostCommand> {
|
||||
|
||||
@Test
|
||||
void testSuccess_deletedHost() throws Exception {
|
||||
persistDeletedHost("ns1.example.tld", now.minusDays(1));
|
||||
persistDeletedHost("ns1.example.tld", fakeClock.nowUtc().minusDays(1));
|
||||
runCommand("ns1.example.tld");
|
||||
assertInStdout("Host 'ns1.example.tld' does not exist or is deleted");
|
||||
}
|
||||
@@ -91,8 +88,11 @@ class GetHostCommandTest extends CommandTestCase<GetHostCommand> {
|
||||
@Test
|
||||
void testSuccess_hostDeletedInFuture() throws Exception {
|
||||
persistResource(
|
||||
newHost("ns1.example.tld").asBuilder().setDeletionTime(now.plusDays(1)).build());
|
||||
runCommand("ns1.example.tld", "--read_timestamp=" + now.plusMonths(1));
|
||||
newHost("ns1.example.tld")
|
||||
.asBuilder()
|
||||
.setDeletionTime(fakeClock.nowUtc().plusDays(1))
|
||||
.build());
|
||||
runCommand("ns1.example.tld", "--read_timestamp=" + fakeClock.nowUtc().plusMonths(1));
|
||||
assertInStdout("Host 'ns1.example.tld' does not exist or is deleted");
|
||||
}
|
||||
|
||||
|
||||
@@ -28,11 +28,17 @@ import google.registry.model.domain.token.PackagePromotion;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link GetPackagePromotionCommand}. */
|
||||
public class GetPackagePromotionCommandTest extends CommandTestCase<GetPackagePromotionCommand> {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.clock = fakeClock;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess() throws Exception {
|
||||
AllocationToken token =
|
||||
|
||||
@@ -48,6 +48,7 @@ class UniformRapidSuspensionCommandTest
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.clock = fakeClock;
|
||||
// Since the command's history client ID must be CharlestonRoad, resave TheRegistrar that way.
|
||||
persistResource(
|
||||
loadRegistrar("TheRegistrar").asBuilder().setRegistrarId("CharlestonRoad").build());
|
||||
|
||||
@@ -98,11 +98,13 @@ public class CreateSyntheticDomainHistoriesPipelineTest {
|
||||
assertAboutImmutableObjects()
|
||||
.that(syntheticHistory.getDomainBase().get())
|
||||
.isEqualExceptFields(domain, "updateTimestamp");
|
||||
// four total histories, two CREATE and two SYNTHETIC
|
||||
assertThat(loadAllOf(DomainHistory.class)).hasSize(4);
|
||||
|
||||
// shouldn't create any entries on re-run
|
||||
// can create multiple entries if we run it multiple times
|
||||
pipeline.run().waitUntilFinish();
|
||||
assertThat(HistoryEntryDao.loadHistoryObjectsForResource(domain.createVKey())).hasSize(2);
|
||||
// three total histories, two CREATE and one SYNTHETIC
|
||||
assertThat(loadAllOf(DomainHistory.class)).hasSize(3);
|
||||
assertThat(HistoryEntryDao.loadHistoryObjectsForResource(domain.createVKey())).hasSize(3);
|
||||
// six total histories, two CREATE and four SYNTHETIC
|
||||
assertThat(loadAllOf(DomainHistory.class)).hasSize(6);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:add>
|
||||
<domain:contact type="admin">mak21</domain:contact>
|
||||
<domain:contact type="billing">mak21</domain:contact>
|
||||
<domain:contact type="tech">mak21</domain:contact>
|
||||
<domain:status s="serverHold"
|
||||
lang="en">Server hold.</domain:status>
|
||||
</domain:add>
|
||||
</domain:update>
|
||||
</update>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
@@ -62,6 +62,12 @@ configurations {
|
||||
// See https://issues.apache.org/jira/browse/BEAM-8862
|
||||
it.exclude group: 'org.mockito', module: 'mockito-core'
|
||||
}
|
||||
all.each {
|
||||
// log4j has high-profile security vulnerabilities. It's a transitive
|
||||
// dependency used by some Apache Beam packages. Excluding it does not
|
||||
// impact our troubleshooting needs.
|
||||
it.exclude group: 'org.apache.logging.log4j'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -1,185 +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.processors;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
|
||||
import com.squareup.javapoet.AnnotationSpec;
|
||||
import com.squareup.javapoet.ClassName;
|
||||
import com.squareup.javapoet.JavaFile;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.ParameterizedTypeName;
|
||||
import com.squareup.javapoet.TypeName;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.ElementFilter;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/** Abstract processor to generate {@link AttributeConverter} for VKey type. */
|
||||
public abstract class AbstractVKeyProcessor extends AbstractProcessor {
|
||||
|
||||
private static final String CONVERTER_CLASS_NAME_TEMP = "VKeyConverter_%s";
|
||||
// The method with same name should be defined in WithStringVKey and WithLongVKey
|
||||
private static final String CLASS_NAME_SUFFIX_KEY = "classNameSuffix";
|
||||
|
||||
// Method in WithStringVKey and WithLongVKey to indicate that this is a composite key.
|
||||
private static final String COMPOSITE_KEY_KEY = "compositeKey";
|
||||
|
||||
abstract Class<?> getSqlColumnType();
|
||||
|
||||
abstract String getAnnotationSimpleName();
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
annotations.forEach(
|
||||
vKeyAnnotationType -> {
|
||||
ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(vKeyAnnotationType))
|
||||
.forEach(
|
||||
annotatedTypeElement -> {
|
||||
DeclaredType entityType = getDeclaredType(annotatedTypeElement);
|
||||
|
||||
List<AnnotationMirror> actualAnnotation =
|
||||
annotatedTypeElement.getAnnotationMirrors().stream()
|
||||
.filter(
|
||||
annotationType ->
|
||||
annotationType
|
||||
.getAnnotationType()
|
||||
.asElement()
|
||||
.equals(vKeyAnnotationType))
|
||||
.collect(toImmutableList());
|
||||
checkState(
|
||||
actualAnnotation.size() == 1,
|
||||
String.format(
|
||||
"type can have only 1 %s annotation", getAnnotationSimpleName()));
|
||||
String converterClassNameSuffix = "";
|
||||
boolean hasCompositeOfyKey = false;
|
||||
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry :
|
||||
actualAnnotation.get(0).getElementValues().entrySet()) {
|
||||
String keyName = entry.getKey().getSimpleName().toString();
|
||||
Object value = entry.getValue().getValue();
|
||||
if (keyName.equals(CLASS_NAME_SUFFIX_KEY)) {
|
||||
converterClassNameSuffix = ((String) value).trim();
|
||||
} else if (keyName.equals(COMPOSITE_KEY_KEY)) {
|
||||
hasCompositeOfyKey = (Boolean) value;
|
||||
}
|
||||
}
|
||||
if (converterClassNameSuffix.isEmpty()) {
|
||||
converterClassNameSuffix =
|
||||
getTypeUtils().asElement(entityType).getSimpleName().toString();
|
||||
}
|
||||
|
||||
try {
|
||||
createJavaFile(
|
||||
getPackageName(annotatedTypeElement),
|
||||
String.format(CONVERTER_CLASS_NAME_TEMP, converterClassNameSuffix),
|
||||
entityType,
|
||||
hasCompositeOfyKey)
|
||||
.writeTo(processingEnv.getFiler());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
private JavaFile createJavaFile(
|
||||
String packageName,
|
||||
String converterClassName,
|
||||
TypeMirror entityTypeMirror,
|
||||
boolean hasCompositeOfyKey) {
|
||||
TypeName entityType = ClassName.get(entityTypeMirror);
|
||||
|
||||
ParameterizedTypeName attributeConverter =
|
||||
ParameterizedTypeName.get(
|
||||
ClassName.get("google.registry.persistence.converter", "VKeyConverter"),
|
||||
entityType,
|
||||
ClassName.get(getSqlColumnType()));
|
||||
|
||||
MethodSpec getAttributeClass =
|
||||
MethodSpec.methodBuilder("getAttributeClass")
|
||||
.addAnnotation(Override.class)
|
||||
.addModifiers(Modifier.PROTECTED)
|
||||
.returns(
|
||||
ParameterizedTypeName.get(
|
||||
ClassName.get(Class.class), ClassName.get(entityTypeMirror)))
|
||||
.addStatement("return $T.class", entityType)
|
||||
.build();
|
||||
|
||||
TypeSpec.Builder classBuilder =
|
||||
TypeSpec.classBuilder(converterClassName)
|
||||
.addAnnotation(
|
||||
AnnotationSpec.builder(ClassName.get(Converter.class))
|
||||
.addMember("autoApply", "true")
|
||||
.build())
|
||||
.addModifiers(Modifier.FINAL)
|
||||
.superclass(attributeConverter)
|
||||
.addMethod(getAttributeClass);
|
||||
|
||||
// If this is a converter for a composite vkey type, generate an override for the default
|
||||
// {@link google.registry.persistence.VKeyConverter.hasCompositeOfyKey()} method, which returns
|
||||
// false.
|
||||
if (hasCompositeOfyKey) {
|
||||
MethodSpec hasCompositeOfyKeyMethod =
|
||||
MethodSpec.methodBuilder("hasCompositeOfyKey")
|
||||
.addAnnotation(Override.class)
|
||||
.addModifiers(Modifier.PROTECTED)
|
||||
.returns(boolean.class)
|
||||
.addStatement("return true", entityType)
|
||||
.build();
|
||||
|
||||
classBuilder.addMethod(hasCompositeOfyKeyMethod);
|
||||
}
|
||||
|
||||
TypeSpec vKeyConverter = classBuilder.build();
|
||||
return JavaFile.builder(packageName, vKeyConverter).build();
|
||||
}
|
||||
|
||||
private DeclaredType getDeclaredType(Element element) {
|
||||
checkState(element.asType().getKind() == TypeKind.DECLARED, "element is not a DeclaredType");
|
||||
return (DeclaredType) element.asType();
|
||||
}
|
||||
|
||||
private String getPackageName(Element element) {
|
||||
return getElementUtils().getPackageOf(element).getQualifiedName().toString();
|
||||
}
|
||||
|
||||
private Elements getElementUtils() {
|
||||
return processingEnv.getElementUtils();
|
||||
}
|
||||
|
||||
private Types getTypeUtils() {
|
||||
return processingEnv.getTypeUtils();
|
||||
}
|
||||
}
|
||||
@@ -1,37 +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.processors;
|
||||
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.annotation.processing.SupportedSourceVersion;
|
||||
import javax.lang.model.SourceVersion;
|
||||
|
||||
/** Annotation processor for entity that is annotated with WithLongVKey. */
|
||||
@SupportedAnnotationTypes("google.registry.persistence.WithLongVKey")
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||
public class LongVKeyProcessor extends AbstractVKeyProcessor {
|
||||
|
||||
private static final String ANNOTATION_SIMPLE_NAME = "WithLongVKey";
|
||||
|
||||
@Override
|
||||
Class<?> getSqlColumnType() {
|
||||
return Long.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
String getAnnotationSimpleName() {
|
||||
return ANNOTATION_SIMPLE_NAME;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +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.processors;
|
||||
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.annotation.processing.SupportedSourceVersion;
|
||||
import javax.lang.model.SourceVersion;
|
||||
|
||||
/** Annotation processor for entity that is annotated with WithStringVKey. */
|
||||
@SupportedAnnotationTypes("google.registry.persistence.WithStringVKey")
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||
public class StringVKeyProcessor extends AbstractVKeyProcessor {
|
||||
|
||||
private static final String ANNOTATION_SIMPLE_NAME = "WithStringVKey";
|
||||
|
||||
@Override
|
||||
Class<?> getSqlColumnType() {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
String getAnnotationSimpleName() {
|
||||
return ANNOTATION_SIMPLE_NAME;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
// 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.processors;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
|
||||
import com.squareup.javapoet.AnnotationSpec;
|
||||
import com.squareup.javapoet.ClassName;
|
||||
import com.squareup.javapoet.JavaFile;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.ParameterizedTypeName;
|
||||
import com.squareup.javapoet.TypeName;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.annotation.processing.SupportedSourceVersion;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.util.ElementFilter;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/** Processor to generate {@link AttributeConverter} for {@code VKey} type. */
|
||||
@SupportedAnnotationTypes("google.registry.persistence.WithVKey")
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||
public class VKeyProcessor extends AbstractProcessor {
|
||||
|
||||
private static final String CONVERTER_CLASS_NAME_TEMP = "VKeyConverter_%s";
|
||||
|
||||
private static final String VKEY_TYPE_METHOD_NAME = "value";
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
annotations.forEach(
|
||||
vKeyAnnotationType ->
|
||||
ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(vKeyAnnotationType))
|
||||
.forEach(
|
||||
annotatedTypeElement -> {
|
||||
DeclaredType entityType = getDeclaredType(annotatedTypeElement);
|
||||
String simpleTypeName =
|
||||
getTypeUtils().asElement(entityType).getSimpleName().toString();
|
||||
List<AnnotationMirror> actualAnnotations =
|
||||
annotatedTypeElement.getAnnotationMirrors().stream()
|
||||
.filter(
|
||||
annotationType ->
|
||||
annotationType
|
||||
.getAnnotationType()
|
||||
.asElement()
|
||||
.equals(vKeyAnnotationType))
|
||||
.collect(toImmutableList());
|
||||
checkState(
|
||||
actualAnnotations.size() == 1,
|
||||
"% can have only one @WithVKey annotation",
|
||||
simpleTypeName);
|
||||
TypeName keyType = null;
|
||||
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry :
|
||||
actualAnnotations.get(0).getElementValues().entrySet()) {
|
||||
String keyName = entry.getKey().getSimpleName().toString();
|
||||
if (keyName.equals(VKEY_TYPE_METHOD_NAME)) {
|
||||
try {
|
||||
keyType =
|
||||
TypeName.get(Class.forName(entry.getValue().getValue().toString()));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(
|
||||
String.format(
|
||||
"VKey key class %s is not valid",
|
||||
entry.getValue().getValue().toString()),
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
createJavaFile(
|
||||
getPackageName(annotatedTypeElement),
|
||||
String.format(CONVERTER_CLASS_NAME_TEMP, simpleTypeName),
|
||||
TypeName.get(entityType),
|
||||
keyType)
|
||||
.writeTo(processingEnv.getFiler());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}));
|
||||
return false;
|
||||
}
|
||||
|
||||
private static JavaFile createJavaFile(
|
||||
String packageName, String converterClassName, TypeName entityType, TypeName keyType) {
|
||||
ParameterizedTypeName attributeConverter =
|
||||
ParameterizedTypeName.get(
|
||||
ClassName.get("google.registry.persistence.converter", "VKeyConverter"),
|
||||
entityType,
|
||||
keyType);
|
||||
|
||||
MethodSpec getEntityClass =
|
||||
MethodSpec.methodBuilder("getEntityClass")
|
||||
.addAnnotation(Override.class)
|
||||
.addModifiers(Modifier.PROTECTED)
|
||||
.returns(ParameterizedTypeName.get(ClassName.get(Class.class), entityType))
|
||||
.addStatement("return $T.class", entityType)
|
||||
.build();
|
||||
|
||||
MethodSpec getKeyClass =
|
||||
MethodSpec.methodBuilder("getKeyClass")
|
||||
.addAnnotation(Override.class)
|
||||
.addModifiers(Modifier.PROTECTED)
|
||||
.returns(ParameterizedTypeName.get(ClassName.get(Class.class), keyType))
|
||||
.addStatement("return $T.class", keyType)
|
||||
.build();
|
||||
|
||||
TypeSpec.Builder classBuilder =
|
||||
TypeSpec.classBuilder(converterClassName)
|
||||
.addAnnotation(
|
||||
AnnotationSpec.builder(ClassName.get(Converter.class))
|
||||
.addMember("autoApply", "true")
|
||||
.build())
|
||||
.addModifiers(Modifier.FINAL)
|
||||
.superclass(attributeConverter)
|
||||
.addMethod(getEntityClass)
|
||||
.addMethod(getKeyClass);
|
||||
|
||||
TypeSpec vKeyConverter = classBuilder.build();
|
||||
return JavaFile.builder(packageName, vKeyConverter).build();
|
||||
}
|
||||
|
||||
private static DeclaredType getDeclaredType(Element element) {
|
||||
checkState(element.asType().getKind() == TypeKind.DECLARED, "element is not a DeclaredType");
|
||||
return (DeclaredType) element.asType();
|
||||
}
|
||||
|
||||
private String getPackageName(Element element) {
|
||||
return getElementUtils().getPackageOf(element).getQualifiedName().toString();
|
||||
}
|
||||
|
||||
private Elements getElementUtils() {
|
||||
return processingEnv.getElementUtils();
|
||||
}
|
||||
|
||||
private Types getTypeUtils() {
|
||||
return processingEnv.getTypeUtils();
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1 @@
|
||||
google.registry.processors.StringVKeyProcessor
|
||||
google.registry.processors.LongVKeyProcessor
|
||||
google.registry.processors.VKeyProcessor
|
||||
@@ -14,8 +14,12 @@
|
||||
|
||||
package google.registry.util;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Iterables.toArray;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.util.EmailMessage.Attachment;
|
||||
import java.io.IOException;
|
||||
@@ -44,6 +48,10 @@ public class SendEmailService {
|
||||
private final Retrier retrier;
|
||||
private final TransportEmailSender transportEmailSender;
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final ImmutableSet<String> ALLOWED_ENVS =
|
||||
ImmutableSet.of("PRODUCTION", "UNITTEST");
|
||||
|
||||
@Inject
|
||||
SendEmailService(Retrier retrier, TransportEmailSender transportEmailSender) {
|
||||
this.retrier = retrier;
|
||||
@@ -55,37 +63,50 @@ public class SendEmailService {
|
||||
* on transient failures.
|
||||
*/
|
||||
public void sendEmail(EmailMessage emailMessage) {
|
||||
retrier.callWithRetry(
|
||||
() -> {
|
||||
Message msg =
|
||||
new MimeMessage(
|
||||
Session.getDefaultInstance(new Properties(), /* authenticator= */ null));
|
||||
msg.setFrom(emailMessage.from());
|
||||
msg.addRecipients(
|
||||
RecipientType.TO, toArray(emailMessage.recipients(), InternetAddress.class));
|
||||
msg.setSubject(emailMessage.subject());
|
||||
if (!ALLOWED_ENVS.contains(
|
||||
Ascii.toUpperCase(System.getProperty("google.registry.environment", "UNITTEST")))) {
|
||||
logger.atInfo().log(
|
||||
String.format(
|
||||
"Email with subject %s would have been sent to recipients %s",
|
||||
emailMessage.subject().substring(0, Math.min(emailMessage.subject().length(), 15)),
|
||||
String.join(
|
||||
" , ",
|
||||
emailMessage.recipients().stream()
|
||||
.map(ia -> ia.toString())
|
||||
.collect(toImmutableSet()))));
|
||||
} else {
|
||||
retrier.callWithRetry(
|
||||
() -> {
|
||||
Message msg =
|
||||
new MimeMessage(
|
||||
Session.getDefaultInstance(new Properties(), /* authenticator= */ null));
|
||||
msg.setFrom(emailMessage.from());
|
||||
msg.addRecipients(
|
||||
RecipientType.TO, toArray(emailMessage.recipients(), InternetAddress.class));
|
||||
msg.setSubject(emailMessage.subject());
|
||||
|
||||
Multipart multipart = new MimeMultipart();
|
||||
BodyPart bodyPart = new MimeBodyPart();
|
||||
bodyPart.setContent(
|
||||
emailMessage.body(),
|
||||
emailMessage.contentType().orElse(MediaType.PLAIN_TEXT_UTF_8).toString());
|
||||
multipart.addBodyPart(bodyPart);
|
||||
Multipart multipart = new MimeMultipart();
|
||||
BodyPart bodyPart = new MimeBodyPart();
|
||||
bodyPart.setContent(
|
||||
emailMessage.body(),
|
||||
emailMessage.contentType().orElse(MediaType.PLAIN_TEXT_UTF_8).toString());
|
||||
multipart.addBodyPart(bodyPart);
|
||||
|
||||
if (emailMessage.attachment().isPresent()) {
|
||||
Attachment attachment = emailMessage.attachment().get();
|
||||
BodyPart attachmentPart = new MimeBodyPart();
|
||||
attachmentPart.setContent(attachment.content(), attachment.contentType().toString());
|
||||
attachmentPart.setFileName(attachment.filename());
|
||||
multipart.addBodyPart(attachmentPart);
|
||||
}
|
||||
msg.addRecipients(RecipientType.BCC, toArray(emailMessage.bccs(), Address.class));
|
||||
msg.addRecipients(RecipientType.CC, toArray(emailMessage.ccs(), Address.class));
|
||||
msg.setContent(multipart);
|
||||
msg.saveChanges();
|
||||
transportEmailSender.sendMessage(msg);
|
||||
},
|
||||
IOException.class,
|
||||
MessagingException.class);
|
||||
if (emailMessage.attachment().isPresent()) {
|
||||
Attachment attachment = emailMessage.attachment().get();
|
||||
BodyPart attachmentPart = new MimeBodyPart();
|
||||
attachmentPart.setContent(attachment.content(), attachment.contentType().toString());
|
||||
attachmentPart.setFileName(attachment.filename());
|
||||
multipart.addBodyPart(attachmentPart);
|
||||
}
|
||||
msg.addRecipients(RecipientType.BCC, toArray(emailMessage.bccs(), Address.class));
|
||||
msg.addRecipients(RecipientType.CC, toArray(emailMessage.ccs(), Address.class));
|
||||
msg.setContent(multipart);
|
||||
msg.saveChanges();
|
||||
transportEmailSender.sendMessage(msg);
|
||||
},
|
||||
IOException.class,
|
||||
MessagingException.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user