From 936609393a671dfdf41cfdaf050104c615174690 Mon Sep 17 00:00:00 2001 From: Gus Brodman Date: Fri, 20 Feb 2026 14:06:59 -0500 Subject: [PATCH] Remove the concept of a TransferData abstract class The only type of thing that can be transferred now is a domain, so there's no point in having this abstract class / redirection. This does not include deletion of the contact-response-related XML classes; that can come next. --- .../domain/DomainTransferRequestFlow.java | 2 +- .../flows/domain/DomainTransferUtils.java | 13 +- .../google/registry/model/EppResource.java | 22 -- .../registry/model/ResourceTransferUtils.java | 19 +- .../registry/model/billing/BillingBase.java | 2 +- .../registry/model/domain/DomainBase.java | 10 +- .../google/registry/model/host/HostBase.java | 5 +- .../registry/model/poll/PollMessage.java | 31 +-- .../model/transfer/BaseTransferObject.java | 2 +- .../model/transfer/ContactTransferData.java | 48 ---- .../model/transfer/DomainTransferData.java | 151 +++++++++++-- .../registry/model/transfer/TransferData.java | 205 ------------------ .../registry/rde/DomainToXjcConverter.java | 3 +- .../domain/DomainTransferFlowTestCase.java | 5 +- .../domain/DomainTransferRejectFlowTest.java | 4 +- ...aTest.java => DomainTransferDataTest.java} | 4 +- .../sql/er_diagram/brief_er_diagram.html | 4 +- .../sql/er_diagram/full_er_diagram.html | 4 +- .../sql/schema/db-schema.sql.generated | 24 +- 19 files changed, 186 insertions(+), 372 deletions(-) delete mode 100644 core/src/main/java/google/registry/model/transfer/ContactTransferData.java delete mode 100644 core/src/main/java/google/registry/model/transfer/TransferData.java rename core/src/test/java/google/registry/model/transfer/{TransferDataTest.java => DomainTransferDataTest.java} (97%) diff --git a/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java b/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java index bd92e541b46..db7ce17bfe5 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java @@ -76,7 +76,7 @@ import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.model.tld.Tld; import google.registry.model.transfer.DomainTransferData; -import google.registry.model.transfer.TransferData.TransferServerApproveEntity; +import google.registry.model.transfer.DomainTransferData.TransferServerApproveEntity; import google.registry.model.transfer.TransferResponse.DomainTransferResponse; import google.registry.model.transfer.TransferStatus; import jakarta.inject.Inject; diff --git a/core/src/main/java/google/registry/flows/domain/DomainTransferUtils.java b/core/src/main/java/google/registry/flows/domain/DomainTransferUtils.java index d190dcad9f1..60000b224d5 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainTransferUtils.java +++ b/core/src/main/java/google/registry/flows/domain/DomainTransferUtils.java @@ -36,8 +36,7 @@ import google.registry.model.reporting.HistoryEntry.HistoryEntryId; import google.registry.model.tld.Tld; import google.registry.model.transfer.DomainTransferData; -import google.registry.model.transfer.TransferData; -import google.registry.model.transfer.TransferData.TransferServerApproveEntity; +import google.registry.model.transfer.DomainTransferData.TransferServerApproveEntity; import google.registry.model.transfer.TransferResponse.DomainTransferResponse; import google.registry.model.transfer.TransferStatus; import google.registry.persistence.VKey; @@ -51,7 +50,7 @@ */ public final class DomainTransferUtils { - /** Sets up {@link TransferData} for a domain with links to entities for server approval. */ + /** Sets up {@link DomainTransferData} for a domain with links to entities for server approval. */ public static DomainTransferData createPendingTransferData( String domainRepoId, Long historyId, @@ -179,7 +178,7 @@ public static ImmutableSet createTransferServerAppr /** Create a poll message for the gaining client in a transfer. */ public static PollMessage createGainingTransferPollMessage( String targetId, - TransferData transferData, + DomainTransferData transferData, @Nullable DateTime extendedRegistrationExpirationTime, DateTime now, HistoryEntryId domainHistoryId) { @@ -202,7 +201,7 @@ public static PollMessage createGainingTransferPollMessage( /** Create a poll message for the losing client in a transfer. */ public static PollMessage createLosingTransferPollMessage( String targetId, - TransferData transferData, + DomainTransferData transferData, @Nullable DateTime extendedRegistrationExpirationTime, HistoryEntryId domainHistoryId) { return new PollMessage.OneTime.Builder() @@ -216,10 +215,10 @@ public static PollMessage createLosingTransferPollMessage( .build(); } - /** Create a {@link DomainTransferResponse} off of the info in a {@link TransferData}. */ + /** Create a {@link DomainTransferResponse} off of the info in a {@link DomainTransferData}. */ static DomainTransferResponse createTransferResponse( String targetId, - TransferData transferData, + DomainTransferData transferData, @Nullable DateTime extendedRegistrationExpirationTime) { return new DomainTransferResponse.Builder() .setDomainName(targetId) diff --git a/core/src/main/java/google/registry/model/EppResource.java b/core/src/main/java/google/registry/model/EppResource.java index beda5bc8d0d..ee5c376ef63 100644 --- a/core/src/main/java/google/registry/model/EppResource.java +++ b/core/src/main/java/google/registry/model/EppResource.java @@ -34,7 +34,6 @@ import com.google.gson.annotations.Expose; import google.registry.config.RegistryConfig; import google.registry.model.eppcommon.StatusValue; -import google.registry.model.transfer.TransferData; import google.registry.persistence.VKey; import google.registry.util.NonFinalForTesting; import jakarta.persistence.Access; @@ -207,27 +206,6 @@ public DateTime getDeletionTime() { /** EppResources that are loaded via foreign keys should implement this marker interface. */ public interface ForeignKeyedEppResource {} - /** An interface for resources that have transfer data. */ - public interface ResourceWithTransferData { - T getTransferData(); - - /** - * The time that this resource was last transferred. - * - *

Can be null if the resource has never been transferred. - */ - DateTime getLastTransferTime(); - } - - /** An interface for builders of resources that have transfer data. */ - public interface BuilderWithTransferData< - T extends TransferData, B extends BuilderWithTransferData> { - B setTransferData(T transferData); - - /** Set the time when this resource was transferred. */ - B setLastTransferTime(DateTime lastTransferTime); - } - /** Abstract builder for {@link EppResource} types. */ public abstract static class Builder> extends GenericBuilder { diff --git a/core/src/main/java/google/registry/model/ResourceTransferUtils.java b/core/src/main/java/google/registry/model/ResourceTransferUtils.java index dabc9476385..c6a70026622 100644 --- a/core/src/main/java/google/registry/model/ResourceTransferUtils.java +++ b/core/src/main/java/google/registry/model/ResourceTransferUtils.java @@ -29,7 +29,6 @@ import google.registry.model.poll.PollMessage; import google.registry.model.reporting.HistoryEntry; import google.registry.model.transfer.DomainTransferData; -import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferResponse; import google.registry.model.transfer.TransferResponse.DomainTransferResponse; import google.registry.model.transfer.TransferStatus; @@ -79,7 +78,7 @@ public static void handlePendingTransferOnDelete( if (!domain.getStatusValues().contains(StatusValue.PENDING_TRANSFER)) { return; } - TransferData oldTransferData = domain.getTransferData(); + DomainTransferData oldTransferData = domain.getTransferData(); tm().delete(oldTransferData.getServerApproveEntities()); tm().put( new PollMessage.OneTime.Builder() @@ -99,8 +98,8 @@ public static void handlePendingTransferOnDelete( * Turn a domain into a builder with its pending transfer resolved. * *

This removes the {@link StatusValue#PENDING_TRANSFER} status, sets the {@link - * TransferStatus}, clears all the server-approve fields on the {@link TransferData}, and sets the - * expiration time of the last pending transfer to now. + * TransferStatus}, clears all the server-approve fields on the {@link DomainTransferData}, and + * sets the expiration time of the last pending transfer to now. */ private static Domain.Builder resolvePendingTransfer( Domain domain, TransferStatus transferStatus, DateTime now) { @@ -125,9 +124,9 @@ private static Domain.Builder resolvePendingTransfer( * Resolve a pending transfer by awarding it to the gaining client. * *

This removes the {@link StatusValue#PENDING_TRANSFER} status, sets the {@link - * TransferStatus}, clears all the server-approve fields on the {@link TransferData}, sets the new - * client id, and sets the last transfer time and the expiration time of the last pending transfer - * to now. + * TransferStatus}, clears all the server-approve fields on the {@link DomainTransferData}, sets + * the new client id, and sets the last transfer time and the expiration time of the last pending + * transfer to now. */ public static Domain approvePendingTransfer( Domain domain, TransferStatus transferStatus, DateTime now) { @@ -143,9 +142,9 @@ public static Domain approvePendingTransfer( * Resolve a pending transfer by denying it. * *

This removes the {@link StatusValue#PENDING_TRANSFER} status, sets the {@link - * TransferStatus}, clears all the server-approve fields on the {@link TransferData}, sets the - * expiration time of the last pending transfer to now, sets the last EPP update time to now, and - * sets the last EPP update client id to the given client id. + * TransferStatus}, clears all the server-approve fields on the {@link DomainTransferData}, sets + * the expiration time of the last pending transfer to now, sets the last EPP update time to now, + * and sets the last EPP update client id to the given client id. */ public static Domain denyPendingTransfer( Domain domain, TransferStatus transferStatus, DateTime now, String lastEppUpdateRegistrarId) { diff --git a/core/src/main/java/google/registry/model/billing/BillingBase.java b/core/src/main/java/google/registry/model/billing/BillingBase.java index 90914264eda..d0463052e2d 100644 --- a/core/src/main/java/google/registry/model/billing/BillingBase.java +++ b/core/src/main/java/google/registry/model/billing/BillingBase.java @@ -26,7 +26,7 @@ import google.registry.model.annotations.IdAllocation; import google.registry.model.domain.DomainHistory; import google.registry.model.reporting.HistoryEntry.HistoryEntryId; -import google.registry.model.transfer.TransferData.TransferServerApproveEntity; +import google.registry.model.transfer.DomainTransferData.TransferServerApproveEntity; import google.registry.persistence.VKey; import jakarta.persistence.Column; import jakarta.persistence.EnumType; diff --git a/core/src/main/java/google/registry/model/domain/DomainBase.java b/core/src/main/java/google/registry/model/domain/DomainBase.java index 6c753c4581d..8aa9af4fb15 100644 --- a/core/src/main/java/google/registry/model/domain/DomainBase.java +++ b/core/src/main/java/google/registry/model/domain/DomainBase.java @@ -43,7 +43,6 @@ import com.google.gson.annotations.Expose; import google.registry.flows.ResourceFlowUtils; import google.registry.model.EppResource; -import google.registry.model.EppResource.ResourceWithTransferData; import google.registry.model.billing.BillingRecurrence; import google.registry.model.domain.launch.LaunchNotice; import google.registry.model.domain.rgp.GracePeriodStatus; @@ -96,8 +95,7 @@ @MappedSuperclass @Embeddable @Access(AccessType.FIELD) -public class DomainBase extends EppResource - implements ResourceWithTransferData { +public class DomainBase extends EppResource { /** The max number of years that a domain can be registered for, as set by ICANN policy. */ public static final int MAX_REGISTRATION_YEARS = 10; @@ -319,12 +317,10 @@ public Optional getAutorenewEndTime() { return Optional.ofNullable(autorenewEndTime.equals(END_OF_TIME) ? null : autorenewEndTime); } - @Override public DomainTransferData getTransferData() { return Optional.ofNullable(transferData).orElse(DomainTransferData.EMPTY); } - @Override public DateTime getLastTransferTime() { return lastTransferTime; } @@ -605,7 +601,7 @@ public VKey createVKey() { /** A builder for constructing {@link Domain}, since it is immutable. */ public static class Builder> - extends EppResource.Builder implements BuilderWithTransferData { + extends EppResource.Builder { public Builder() {} @@ -783,13 +779,11 @@ public B setAutorenewEndTime(Optional autorenewEndTime) { return thisCastToDerived(); } - @Override public B setTransferData(DomainTransferData transferData) { getInstance().transferData = transferData; return thisCastToDerived(); } - @Override public B setLastTransferTime(DateTime lastTransferTime) { getInstance().lastTransferTime = lastTransferTime; return thisCastToDerived(); diff --git a/core/src/main/java/google/registry/model/host/HostBase.java b/core/src/main/java/google/registry/model/host/HostBase.java index e8af4d3aafd..11824ef96c5 100644 --- a/core/src/main/java/google/registry/model/host/HostBase.java +++ b/core/src/main/java/google/registry/model/host/HostBase.java @@ -24,7 +24,6 @@ import com.google.common.collect.ImmutableSet; import google.registry.model.EppResource; import google.registry.model.domain.Domain; -import google.registry.model.transfer.TransferData; import google.registry.persistence.VKey; import google.registry.persistence.converter.InetAddressSetUserType; import jakarta.persistence.Access; @@ -41,8 +40,8 @@ /** * A persistable Host resource including mutable and non-mutable fields. * - *

A host's {@link TransferData} is stored on the superordinate domain. Non-subordinate hosts - * don't carry a full set of TransferData; all they have is lastTransferTime. + *

A host's full transfer data is stored on the superordinate domain. Non-subordinate hosts don't + * carry a full set of TransferData; all they have is lastTransferTime. * *

This class deliberately does not include an {@link jakarta.persistence.Id} so that any * foreign-keyed fields can refer to the proper parent entity's ID, whether we're storing this in diff --git a/core/src/main/java/google/registry/model/poll/PollMessage.java b/core/src/main/java/google/registry/model/poll/PollMessage.java index 47600c23963..1c1f2b6cf86 100644 --- a/core/src/main/java/google/registry/model/poll/PollMessage.java +++ b/core/src/main/java/google/registry/model/poll/PollMessage.java @@ -32,14 +32,12 @@ import google.registry.model.eppoutput.EppResponse.ResponseData; import google.registry.model.host.Host; import google.registry.model.host.HostHistory; -import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse; import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse; import google.registry.model.poll.PendingActionNotificationResponse.HostPendingActionNotificationResponse; import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry.HistoryEntryId; -import google.registry.model.transfer.TransferData.TransferServerApproveEntity; +import google.registry.model.transfer.DomainTransferData.TransferServerApproveEntity; 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.WithVKey; @@ -406,12 +404,8 @@ void postLoad() { if (pendingActionNotificationResponse != null) { // Promote the pending action notification response to its specialized type. if (contactId != null) { - pendingActionNotificationResponse = - ContactPendingActionNotificationResponse.create( - pendingActionNotificationResponse.nameOrId.value, - pendingActionNotificationResponse.getActionResult(), - pendingActionNotificationResponse.getTrid(), - pendingActionNotificationResponse.processedDate); + // Contacts are no longer supported + pendingActionNotificationResponse = null; } else if (domainName != null) { pendingActionNotificationResponse = DomainPendingActionNotificationResponse.create( @@ -432,16 +426,8 @@ void postLoad() { // The transferResponse is currently an unspecialized TransferResponse instance, create the // appropriate subclass so that the value is consistently specialized if (contactId != null) { - transferResponse = - new ContactTransferResponse.Builder() - .setContactId(contactId) - .setGainingRegistrarId(transferResponse.getGainingRegistrarId()) - .setLosingRegistrarId(transferResponse.getLosingRegistrarId()) - .setTransferStatus(transferResponse.getTransferStatus()) - .setTransferRequestTime(transferResponse.getTransferRequestTime()) - .setPendingTransferExpirationTime( - transferResponse.getPendingTransferExpirationTime()) - .build(); + // Contacts are no longer supported + transferResponse = null; } else if (domainName != null) { transferResponse = new DomainTransferResponse.Builder() @@ -488,9 +474,6 @@ public Builder setResponseData(ImmutableList responseDat // Set identifier fields based on the type of the notification response. if (instance.pendingActionNotificationResponse - instanceof ContactPendingActionNotificationResponse) { - instance.contactId = instance.pendingActionNotificationResponse.nameOrId.value; - } else if (instance.pendingActionNotificationResponse instanceof DomainPendingActionNotificationResponse) { instance.domainName = instance.pendingActionNotificationResponse.nameOrId.value; } else if (instance.pendingActionNotificationResponse @@ -507,9 +490,7 @@ public Builder setResponseData(ImmutableList responseDat .orElse(null); // Set the identifier according to the TransferResponse type. - if (instance.transferResponse instanceof ContactTransferResponse) { - instance.contactId = ((ContactTransferResponse) instance.transferResponse).getContactId(); - } else if (instance.transferResponse instanceof DomainTransferResponse response) { + if (instance.transferResponse instanceof DomainTransferResponse response) { instance.domainName = response.getDomainName(); instance.extendedRegistrationExpirationTime = response.getExtendedRegistrationExpirationTime(); diff --git a/core/src/main/java/google/registry/model/transfer/BaseTransferObject.java b/core/src/main/java/google/registry/model/transfer/BaseTransferObject.java index 1c68e7be3ee..9c55b52a1a9 100644 --- a/core/src/main/java/google/registry/model/transfer/BaseTransferObject.java +++ b/core/src/main/java/google/registry/model/transfer/BaseTransferObject.java @@ -25,7 +25,7 @@ import jakarta.xml.bind.annotation.XmlTransient; import org.joda.time.DateTime; -/** Fields common to {@link TransferData} and {@link TransferResponse}. */ +/** Fields common to {@link DomainTransferData} and {@link TransferResponse}. */ @XmlTransient @MappedSuperclass public abstract class BaseTransferObject extends ImmutableObject implements UnsafeSerializable { diff --git a/core/src/main/java/google/registry/model/transfer/ContactTransferData.java b/core/src/main/java/google/registry/model/transfer/ContactTransferData.java deleted file mode 100644 index 5a13695c9bd..00000000000 --- a/core/src/main/java/google/registry/model/transfer/ContactTransferData.java +++ /dev/null @@ -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.transfer; - -import jakarta.persistence.Embeddable; - -/** Transfer data for contact. */ -@Embeddable -public class ContactTransferData extends TransferData { - public static final ContactTransferData EMPTY = new ContactTransferData(); - - @Override - public boolean isEmpty() { - return EMPTY.equals(this); - } - - @Override - protected Builder createEmptyBuilder() { - return new Builder(); - } - - @Override - public Builder asBuilder() { - return new Builder(clone(this)); - } - - public static class Builder extends TransferData.Builder { - /** Create a {@link Builder} wrapping a new instance. */ - public Builder() {} - - /** Create a {@link Builder} wrapping the given instance. */ - private Builder(ContactTransferData instance) { - super(instance); - } - } -} diff --git a/core/src/main/java/google/registry/model/transfer/DomainTransferData.java b/core/src/main/java/google/registry/model/transfer/DomainTransferData.java index 13d9e773630..7eb4d9a714f 100644 --- a/core/src/main/java/google/registry/model/transfer/DomainTransferData.java +++ b/core/src/main/java/google/registry/model/transfer/DomainTransferData.java @@ -14,14 +14,20 @@ package google.registry.model.transfer; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.ImmutableList.toImmutableList; import static google.registry.util.CollectionUtils.isNullOrEmpty; +import static google.registry.util.CollectionUtils.nullToEmpty; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import google.registry.model.Buildable; import google.registry.model.billing.BillingCancellation; import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingRecurrence; import google.registry.model.domain.Period; import google.registry.model.domain.Period.Unit; +import google.registry.model.eppcommon.Trid; import google.registry.model.poll.PollMessage; import google.registry.persistence.VKey; import google.registry.util.NullIgnoringCollectionBuilder; @@ -36,9 +42,41 @@ /** Transfer data for domain. */ @Embeddable -public class DomainTransferData extends TransferData { +public class DomainTransferData extends BaseTransferObject implements Buildable { public static final DomainTransferData EMPTY = new DomainTransferData(); + /** The transaction id of the most recent transfer request (or null if there never was one). */ + @Embedded + @AttributeOverrides({ + @AttributeOverride( + name = "serverTransactionId", + column = @Column(name = "transfer_server_txn_id")), + @AttributeOverride( + name = "clientTransactionId", + column = @Column(name = "transfer_client_txn_id")) + }) + Trid transferRequestTrid; + + @Column(name = "transfer_repo_id") + String repoId; + + @Column(name = "transfer_history_entry_id") + Long historyEntryId; + + // The pollMessageId1 and pollMessageId2 are used to store the IDs for gaining and losing poll + // messages in Cloud SQL. + // + // In addition, there may be a third poll message for the autorenew poll message on domain + // transfer if applicable. + @Column(name = "transfer_poll_message_id_1") + Long pollMessageId1; + + @Column(name = "transfer_poll_message_id_2") + Long pollMessageId2; + + @Column(name = "transfer_poll_message_id_3") + Long pollMessageId3; + /** * The period to extend the registration upon completion of the transfer. * @@ -107,15 +145,19 @@ public class DomainTransferData extends TransferData { @Column(name = "transfer_autorenew_poll_message_history_id") Long serverApproveAutorenewPollMessageHistoryId; - @Override - public Builder copyConstantFieldsToBuilder() { - return ((Builder) super.copyConstantFieldsToBuilder()).setTransferPeriod(transferPeriod); - } - public Period getTransferPeriod() { return transferPeriod; } + public Long getHistoryEntryId() { + return historyEntryId; + } + + @Nullable + public Trid getTransferRequestTrid() { + return transferRequestTrid; + } + @Nullable public DateTime getTransferredRegistrationExpirationTime() { return transferredRegistrationExpirationTime; @@ -141,12 +183,13 @@ public Long getServerApproveAutorenewPollMessageHistoryId() { return serverApproveAutorenewPollMessageHistoryId; } - @Override public ImmutableSet> getServerApproveEntities() { ImmutableSet.Builder> builder = new ImmutableSet.Builder<>(); - builder.addAll(super.getServerApproveEntities()); return NullIgnoringCollectionBuilder.create(builder) + .add(pollMessageId1 != null ? VKey.create(PollMessage.class, pollMessageId1) : null) + .add(pollMessageId2 != null ? VKey.create(PollMessage.class, pollMessageId2) : null) + .add(pollMessageId3 != null ? VKey.create(PollMessage.class, pollMessageId3) : null) .add(serverApproveBillingEvent) .add(serverApproveAutorenewEvent) .add(billingCancellationId) @@ -154,16 +197,10 @@ public ImmutableSet> getServerApprov .build(); } - @Override public boolean isEmpty() { return EMPTY.equals(this); } - @Override - protected Builder createEmptyBuilder() { - return new Builder(); - } - @Override public Builder asBuilder() { return new Builder(clone(this)); @@ -186,7 +223,72 @@ static void mapBillingCancellationEntityToField( } } - public static class Builder extends TransferData.Builder { + /** + * Returns a fresh Builder populated only with the constant fields of this TransferData, i.e. + * those that are fixed and unchanging throughout the transfer process. + * + *

These fields are: + * + *

    + *
  • transferRequestTrid + *
  • transferRequestTime + *
  • gainingClientId + *
  • losingClientId + *
  • transferPeriod + *
+ */ + public Builder copyConstantFieldsToBuilder() { + return new Builder() + .setTransferPeriod(transferPeriod) + .setTransferRequestTrid(transferRequestTrid) + .setTransferRequestTime(transferRequestTime) + .setGainingRegistrarId(gainingClientId) + .setLosingRegistrarId(losingClientId); + } + + /** Maps serverApproveEntities set to the individual fields. */ + static void mapServerApproveEntitiesToFields( + Set> serverApproveEntities, + DomainTransferData transferData) { + if (isNullOrEmpty(serverApproveEntities)) { + transferData.pollMessageId1 = null; + transferData.pollMessageId2 = null; + transferData.pollMessageId3 = null; + return; + } + ImmutableList sortedPollMessageIds = getSortedPollMessageIds(serverApproveEntities); + if (!sortedPollMessageIds.isEmpty()) { + transferData.pollMessageId1 = sortedPollMessageIds.get(0); + } + if (sortedPollMessageIds.size() >= 2) { + transferData.pollMessageId2 = sortedPollMessageIds.get(1); + } + if (sortedPollMessageIds.size() >= 3) { + transferData.pollMessageId3 = sortedPollMessageIds.get(2); + } + } + + /** + * Gets poll message IDs from the given serverApproveEntities and sorts the IDs in natural order. + */ + private static ImmutableList getSortedPollMessageIds( + Set> serverApproveEntities) { + return nullToEmpty(serverApproveEntities).stream() + .filter(vKey -> PollMessage.class.isAssignableFrom(vKey.getKind())) + .map(vKey -> (long) vKey.getKey()) + .sorted() + .collect(toImmutableList()); + } + + /** + * Marker interface for objects that are written in anticipation of a server approval, and + * therefore need to be deleted under any other outcome. + */ + public interface TransferServerApproveEntity { + VKey createVKey(); + } + + public static class Builder extends BaseTransferObject.Builder { /** Create a {@link Builder} wrapping a new instance. */ public Builder() {} @@ -195,6 +297,20 @@ private Builder(DomainTransferData instance) { super(instance); } + @Override + public DomainTransferData build() { + if (getInstance().pollMessageId1 != null) { + checkState(getInstance().repoId != null, "Repo id undefined"); + checkState(getInstance().historyEntryId != null, "History entry undefined"); + } + return super.build(); + } + + public Builder setTransferRequestTrid(Trid transferRequestTrid) { + getInstance().transferRequestTrid = transferRequestTrid; + return this; + } + public Builder setTransferPeriod(Period transferPeriod) { getInstance().transferPeriod = transferPeriod; return this; @@ -223,12 +339,13 @@ public Builder setServerApproveAutorenewPollMessage( return this; } - @Override public Builder setServerApproveEntities( String repoId, Long historyId, ImmutableSet> serverApproveEntities) { - super.setServerApproveEntities(repoId, historyId, serverApproveEntities); + getInstance().repoId = repoId; + getInstance().historyEntryId = historyId; + mapServerApproveEntitiesToFields(serverApproveEntities, getInstance()); mapBillingCancellationEntityToField(serverApproveEntities, getInstance()); return this; } diff --git a/core/src/main/java/google/registry/model/transfer/TransferData.java b/core/src/main/java/google/registry/model/transfer/TransferData.java deleted file mode 100644 index e3e374fc9e9..00000000000 --- a/core/src/main/java/google/registry/model/transfer/TransferData.java +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package google.registry.model.transfer; - -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static google.registry.util.CollectionUtils.isNullOrEmpty; -import static google.registry.util.CollectionUtils.nullToEmpty; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import google.registry.model.Buildable; -import google.registry.model.EppResource; -import google.registry.model.eppcommon.Trid; -import google.registry.model.poll.PollMessage; -import google.registry.persistence.VKey; -import google.registry.util.NullIgnoringCollectionBuilder; -import jakarta.persistence.AttributeOverride; -import jakarta.persistence.AttributeOverrides; -import jakarta.persistence.Column; -import jakarta.persistence.Embedded; -import jakarta.persistence.MappedSuperclass; -import java.util.Set; -import javax.annotation.Nullable; - -/** - * Common transfer data for {@link EppResource} types. Only applies to domains and contacts; hosts - * are implicitly transferred with their superordinate domain. - */ -@MappedSuperclass -public abstract class TransferData extends BaseTransferObject implements Buildable { - - /** The transaction id of the most recent transfer request (or null if there never was one). */ - @Embedded - @AttributeOverrides({ - @AttributeOverride( - name = "serverTransactionId", - column = @Column(name = "transfer_server_txn_id")), - @AttributeOverride( - name = "clientTransactionId", - column = @Column(name = "transfer_client_txn_id")) - }) - Trid transferRequestTrid; - - @Column(name = "transfer_repo_id") - String repoId; - - @Column(name = "transfer_history_entry_id") - Long historyEntryId; - - // The pollMessageId1 and pollMessageId2 are used to store the IDs for gaining and losing poll - // messages in Cloud SQL. - // - // In addition, there may be a third poll message for the autorenew poll message on domain - // transfer if applicable. - @Column(name = "transfer_poll_message_id_1") - Long pollMessageId1; - - @Column(name = "transfer_poll_message_id_2") - Long pollMessageId2; - - @Column(name = "transfer_poll_message_id_3") - Long pollMessageId3; - - public abstract boolean isEmpty(); - - public Long getHistoryEntryId() { - return historyEntryId; - } - - @Nullable - public Trid getTransferRequestTrid() { - return transferRequestTrid; - } - - public ImmutableSet> getServerApproveEntities() { - return NullIgnoringCollectionBuilder.create( - new ImmutableSet.Builder>()) - .add(pollMessageId1 != null ? VKey.create(PollMessage.class, pollMessageId1) : null) - .add(pollMessageId2 != null ? VKey.create(PollMessage.class, pollMessageId2) : null) - .add(pollMessageId3 != null ? VKey.create(PollMessage.class, pollMessageId3) : null) - .getBuilder() - .build(); - } - - @Override - public abstract Builder asBuilder(); - - protected abstract Builder createEmptyBuilder(); - - /** - * Returns a fresh Builder populated only with the constant fields of this TransferData, i.e. - * those that are fixed and unchanging throughout the transfer process. - * - *

These fields are: - * - *

    - *
  • transferRequestTrid - *
  • transferRequestTime - *
  • gainingClientId - *
  • losingClientId - *
  • transferPeriod - *
- */ - public Builder copyConstantFieldsToBuilder() { - Builder newBuilder = createEmptyBuilder(); - newBuilder - .setTransferRequestTrid(transferRequestTrid) - .setTransferRequestTime(transferRequestTime) - .setGainingRegistrarId(gainingClientId) - .setLosingRegistrarId(losingClientId); - return newBuilder; - } - - /** Maps serverApproveEntities set to the individual fields. */ - static void mapServerApproveEntitiesToFields( - Set> serverApproveEntities, - TransferData transferData) { - if (isNullOrEmpty(serverApproveEntities)) { - transferData.pollMessageId1 = null; - transferData.pollMessageId2 = null; - transferData.pollMessageId3 = null; - return; - } - ImmutableList sortedPollMessageIds = getSortedPollMessageIds(serverApproveEntities); - if (sortedPollMessageIds.size() >= 1) { - transferData.pollMessageId1 = sortedPollMessageIds.get(0); - } - if (sortedPollMessageIds.size() >= 2) { - transferData.pollMessageId2 = sortedPollMessageIds.get(1); - } - if (sortedPollMessageIds.size() >= 3) { - transferData.pollMessageId3 = sortedPollMessageIds.get(2); - } - } - - /** - * Gets poll message IDs from the given serverApproveEntities and sorted the IDs in natural order. - */ - private static ImmutableList getSortedPollMessageIds( - Set> serverApproveEntities) { - return nullToEmpty(serverApproveEntities).stream() - .filter(vKey -> PollMessage.class.isAssignableFrom(vKey.getKind())) - .map(vKey -> (long) vKey.getKey()) - .sorted() - .collect(toImmutableList()); - } - - /** Builder for {@link TransferData} because it is immutable. */ - public abstract static class Builder> - extends BaseTransferObject.Builder { - - /** Create a {@link Builder} wrapping a new instance. */ - protected Builder() {} - - /** Create a {@link Builder} wrapping the given instance. */ - protected Builder(T instance) { - super(instance); - } - - public B setTransferRequestTrid(Trid transferRequestTrid) { - getInstance().transferRequestTrid = transferRequestTrid; - return thisCastToDerived(); - } - - public B setServerApproveEntities( - String repoId, - Long historyId, - ImmutableSet> serverApproveEntities) { - getInstance().repoId = repoId; - getInstance().historyEntryId = historyId; - mapServerApproveEntitiesToFields(serverApproveEntities, getInstance()); - return thisCastToDerived(); - } - - @Override - public T build() { - if (getInstance().pollMessageId1 != null) { - checkState(getInstance().repoId != null, "Repo id undefined"); - checkState(getInstance().historyEntryId != null, "History entry undefined"); - } - return super.build(); - } - } - - /** - * Marker interface for objects that are written in anticipation of a server approval, and - * therefore need to be deleted under any other outcome. - */ - public interface TransferServerApproveEntity { - VKey createVKey(); - } -} diff --git a/core/src/main/java/google/registry/rde/DomainToXjcConverter.java b/core/src/main/java/google/registry/rde/DomainToXjcConverter.java index af3143d9e2f..fe671a28def 100644 --- a/core/src/main/java/google/registry/rde/DomainToXjcConverter.java +++ b/core/src/main/java/google/registry/rde/DomainToXjcConverter.java @@ -23,7 +23,6 @@ import google.registry.model.eppcommon.StatusValue; import google.registry.model.rde.RdeMode; import google.registry.model.transfer.DomainTransferData; -import google.registry.model.transfer.TransferData; import google.registry.util.Idn; import google.registry.xjc.domain.XjcDomainNsType; import google.registry.xjc.domain.XjcDomainStatusType; @@ -221,7 +220,7 @@ private static boolean hasGainingAndLosingRegistrars(Domain model) { && !Strings.isNullOrEmpty(model.getTransferData().getLosingRegistrarId()); } - /** Converts {@link TransferData} to {@link XjcRdeDomainTransferDataType}. */ + /** Converts {@link DomainTransferData} to {@link XjcRdeDomainTransferDataType}. */ private static XjcRdeDomainTransferDataType convertTransferData(DomainTransferData model) { XjcRdeDomainTransferDataType bean = new XjcRdeDomainTransferDataType(); bean.setTrStatus( diff --git a/core/src/test/java/google/registry/flows/domain/DomainTransferFlowTestCase.java b/core/src/test/java/google/registry/flows/domain/DomainTransferFlowTestCase.java index 094620fd6a9..29a0f234a24 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainTransferFlowTestCase.java +++ b/core/src/test/java/google/registry/flows/domain/DomainTransferFlowTestCase.java @@ -41,7 +41,7 @@ import google.registry.model.host.Host; import google.registry.model.reporting.HistoryEntry; import google.registry.model.tld.Tld; -import google.registry.model.transfer.TransferData; +import google.registry.model.transfer.DomainTransferData; import google.registry.model.transfer.TransferStatus; import google.registry.persistence.transaction.JpaTransactionManagerExtension; import org.joda.time.DateTime; @@ -161,7 +161,8 @@ BillingRecurrence getGainingClientAutorenewEvent() { .build(); } - void assertTransferFailed(Domain domain, TransferStatus status, TransferData oldTransferData) { + void assertTransferFailed( + Domain domain, TransferStatus status, DomainTransferData oldTransferData) { assertAboutDomains() .that(domain) .doesNotHaveStatusValue(StatusValue.PENDING_TRANSFER) diff --git a/core/src/test/java/google/registry/flows/domain/DomainTransferRejectFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainTransferRejectFlowTest.java index e5ab76f612c..f26b460d497 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainTransferRejectFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainTransferRejectFlowTest.java @@ -54,7 +54,7 @@ import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.HistoryEntry; import google.registry.model.tld.Tld; -import google.registry.model.transfer.TransferData; +import google.registry.model.transfer.DomainTransferData; import google.registry.model.transfer.TransferResponse; import google.registry.model.transfer.TransferStatus; import org.joda.time.DateTime; @@ -90,7 +90,7 @@ private void doSuccessfulTest(String commandFilename, String expectedXmlFilename assertMutatingFlow(true); DateTime originalExpirationTime = domain.getRegistrationExpirationTime(); ImmutableSet originalGracePeriods = domain.getGracePeriods(); - TransferData originalTransferData = domain.getTransferData(); + DomainTransferData originalTransferData = domain.getTransferData(); runFlowAssertResponse(loadFile(expectedXmlFilename)); // Transfer should have been rejected. Verify correct fields were set. domain = reloadResourceByForeignKey(); diff --git a/core/src/test/java/google/registry/model/transfer/TransferDataTest.java b/core/src/test/java/google/registry/model/transfer/DomainTransferDataTest.java similarity index 97% rename from core/src/test/java/google/registry/model/transfer/TransferDataTest.java rename to core/src/test/java/google/registry/model/transfer/DomainTransferDataTest.java index e95e2283b66..971cfb36d9f 100644 --- a/core/src/test/java/google/registry/model/transfer/TransferDataTest.java +++ b/core/src/test/java/google/registry/model/transfer/DomainTransferDataTest.java @@ -29,8 +29,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -/** Unit tests for {@link TransferData}. */ -public class TransferDataTest { +/** Unit tests for {@link DomainTransferData}. */ +public class DomainTransferDataTest { private final DateTime now = DateTime.now(UTC); diff --git a/db/src/main/resources/sql/er_diagram/brief_er_diagram.html b/db/src/main/resources/sql/er_diagram/brief_er_diagram.html index 4ac5fafc8e2..1e84b6ab82b 100644 --- a/db/src/main/resources/sql/er_diagram/brief_er_diagram.html +++ b/db/src/main/resources/sql/er_diagram/brief_er_diagram.html @@ -261,7 +261,7 @@

System Information

generated on - 2026-02-20 05:51:31 + 2026-02-20 20:45:48 last flyway file @@ -273,7 +273,7 @@

System Information

 

- SchemaCrawler_Diagram generated by SchemaCrawler 16.27.1 generated on 2026-02-20 05:51:31 + SchemaCrawler_Diagram generated by SchemaCrawler 16.27.1 generated on 2026-02-20 20:45:48 allocationtoken_a08ccbef public."AllocationToken" [table] token text not null domain_name text redemption_domain_repo_id text token_type text diff --git a/db/src/main/resources/sql/er_diagram/full_er_diagram.html b/db/src/main/resources/sql/er_diagram/full_er_diagram.html index 25cdad423da..9c93213cf20 100644 --- a/db/src/main/resources/sql/er_diagram/full_er_diagram.html +++ b/db/src/main/resources/sql/er_diagram/full_er_diagram.html @@ -261,7 +261,7 @@

System Information

generated on - 2026-02-20 05:51:28 + 2026-02-20 20:45:45 last flyway file @@ -273,7 +273,7 @@

System Information

 

- SchemaCrawler_Diagram generated by SchemaCrawler 16.27.1 generated on 2026-02-20 05:51:28 + SchemaCrawler_Diagram generated by SchemaCrawler 16.27.1 generated on 2026-02-20 20:45:45 allocationtoken_a08ccbef public."AllocationToken" [table] token text not null update_timestamp timestamptz allowed_registrar_ids _text allowed_tlds _text creation_time timestamptz not null discount_fraction float8(17, 17) not null discount_premiums bool not null discount_years int4 not null domain_name text redemption_domain_repo_id text token_status_transitions hstore token_type text redemption_domain_history_id int8 renewal_price_behavior text not null registration_behavior text not null allowed_epp_actions _text renewal_price_amount numeric(19, 2) renewal_price_currency text discount_price_amount numeric(19, 2) discount_price_currency text diff --git a/db/src/main/resources/sql/schema/db-schema.sql.generated b/db/src/main/resources/sql/schema/db-schema.sql.generated index 40ad0778489..ce08baf2545 100644 --- a/db/src/main/resources/sql/schema/db-schema.sql.generated +++ b/db/src/main/resources/sql/schema/db-schema.sql.generated @@ -206,20 +206,20 @@ tech_contact text, tld text, transfer_billing_cancellation_id bigint, + transfer_history_entry_id bigint, + transfer_poll_message_id_1 bigint, + transfer_poll_message_id_2 bigint, + transfer_poll_message_id_3 bigint, + transfer_repo_id text, transfer_billing_recurrence_id bigint, transfer_autorenew_poll_message_id bigint, transfer_autorenew_poll_message_history_id bigint, transfer_billing_event_id bigint, transfer_renew_period_unit text check (transfer_renew_period_unit in ('YEARS','MONTHS')), transfer_renew_period_value integer, - transfer_registration_expiration_time timestamp(6) with time zone, - transfer_history_entry_id bigint, - transfer_poll_message_id_1 bigint, - transfer_poll_message_id_2 bigint, - transfer_poll_message_id_3 bigint, - transfer_repo_id text, transfer_client_txn_id text, transfer_server_txn_id text, + transfer_registration_expiration_time timestamp(6) with time zone, transfer_gaining_registrar_id text, transfer_losing_registrar_id text, transfer_pending_expiration_time timestamp(6) with time zone, @@ -278,20 +278,20 @@ tech_contact text, tld text, transfer_billing_cancellation_id bigint, + transfer_history_entry_id bigint, + transfer_poll_message_id_1 bigint, + transfer_poll_message_id_2 bigint, + transfer_poll_message_id_3 bigint, + transfer_repo_id text, transfer_billing_recurrence_id bigint, transfer_autorenew_poll_message_id bigint, transfer_autorenew_poll_message_history_id bigint, transfer_billing_event_id bigint, transfer_renew_period_unit text check (transfer_renew_period_unit in ('YEARS','MONTHS')), transfer_renew_period_value integer, - transfer_registration_expiration_time timestamp(6) with time zone, - transfer_history_entry_id bigint, - transfer_poll_message_id_1 bigint, - transfer_poll_message_id_2 bigint, - transfer_poll_message_id_3 bigint, - transfer_repo_id text, transfer_client_txn_id text, transfer_server_txn_id text, + transfer_registration_expiration_time timestamp(6) with time zone, transfer_gaining_registrar_id text, transfer_losing_registrar_id text, transfer_pending_expiration_time timestamp(6) with time zone,