1
0
mirror of https://github.com/google/nomulus synced 2026-02-23 13:29:06 +00:00

Compare commits

...

1 Commits

Author SHA1 Message Date
gbrodman
ee8746c857 Remove Contact and ContactHistory Java objects and related code (#2964)
This doesn't remove everything -- there are still other contact-related
objects that we'll need to remove (e.g.
ContactPendingActionNotificationResponse) and simplifications we'll need to make
(e.g. only domains can be transferred now, so all transfer data can move
there instead of being generic)

But this removes the bulk of the remaining contact-related code. We'll
keep around the XML request objects, since it's still nice to route them
to the appropriate (exception-throwing but logging) flow class.
2026-02-20 16:22:29 +00:00
33 changed files with 50 additions and 1525 deletions

View File

@@ -67,14 +67,11 @@ import google.registry.flows.EppException.ParameterValueSyntaxErrorException;
import google.registry.flows.EppException.RequiredParameterMissingException;
import google.registry.flows.EppException.StatusProhibitsOperationException;
import google.registry.flows.EppException.UnimplementedOptionException;
import google.registry.flows.exceptions.ContactsProhibitedException;
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
import google.registry.model.EppResource;
import google.registry.model.billing.BillingBase.Flag;
import google.registry.model.billing.BillingBase.Reason;
import google.registry.model.billing.BillingRecurrence;
import google.registry.model.contact.Contact;
import google.registry.model.domain.DesignatedContact;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainCommand.Create;
import google.registry.model.domain.DomainCommand.CreateOrUpdate;
@@ -421,33 +418,6 @@ public class DomainFlowUtils {
}
}
/** Enforces absence of contact data on creation as part of the Minimum Dataset requirements. */
static void enforceContactAbsencesOnCreate(Create create)
throws ParameterValuePolicyErrorException {
enforceContactAbsences(create.getRegistrant(), create.getContacts());
}
/** Enforces absence of contact data on update as part of the Minimum Dataset requirements. */
static void enforceContactAbsencesOnUpdate(Update update)
throws ParameterValuePolicyErrorException {
Set<DesignatedContact> allDesignatedContacts =
Sets.union(update.getInnerAdd().getContacts(), update.getInnerRemove().getContacts());
enforceContactAbsences(update.getInnerChange().getRegistrant(), allDesignatedContacts);
}
/** Enforces the absence of contact data as part of the Minimum Dataset requirements. */
static void enforceContactAbsences(
Optional<VKey<Contact>> registrant, Set<DesignatedContact> contacts)
throws ParameterValuePolicyErrorException {
if (registrant.isPresent()) {
throw new RegistrantProhibitedException();
}
if (!contacts.isEmpty()) {
throw new ContactsProhibitedException();
}
}
static void validateNameserversAllowedOnTld(String tld, Set<String> fullyQualifiedHostNames)
throws EppException {
ImmutableSet<String> allowedHostNames = Tld.get(tld).getAllowedFullyQualifiedHostNames();
@@ -971,7 +941,6 @@ public class DomainFlowUtils {
Create command, Tld tld, InternetDomainName domainName) throws EppException {
verifyNotInPendingDelete(command.getNameservers());
String tldStr = tld.getTldStr();
enforceContactAbsencesOnCreate(command);
ImmutableSet<String> hostNames = command.getNameserverHostNames();
validateNameserversCountForTld(tldStr, domainName, hostNames.size());
validateNameserversAllowedOnTld(tldStr, hostNames);

View File

@@ -29,7 +29,6 @@ import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo;
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences;
import static google.registry.flows.domain.DomainFlowUtils.enforceContactAbsencesOnUpdate;
import static google.registry.flows.domain.DomainFlowUtils.updateDsData;
import static google.registry.flows.domain.DomainFlowUtils.validateDsData;
import static google.registry.flows.domain.DomainFlowUtils.validateFeesAckedIfPresent;
@@ -249,8 +248,6 @@ public final class DomainUpdateFlow implements MutatingFlow {
ext.getRemove().map(Remove::getDsData).orElse(ImmutableSet.of()));
}
Change change = command.getInnerChange();
enforceContactAbsencesOnUpdate(command);
Domain.Builder domainBuilder =
domain
.asBuilder()

View File

@@ -29,7 +29,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import google.registry.config.RegistryConfig;
import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain;
import google.registry.model.host.Host;
import google.registry.persistence.VKey;
@@ -60,7 +59,6 @@ public final class ForeignKeyUtils {
private static final ImmutableMap<Class<? extends EppResource>, String>
RESOURCE_TYPE_TO_FK_PROPERTY =
ImmutableMap.of(
Contact.class, "contactId",
Domain.class, "domainName",
Host.class, "hostName");

View File

@@ -1,96 +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.contact;
import google.registry.model.EppResource.ForeignKeyedEppResource;
import google.registry.model.annotations.ExternalMessagingName;
import google.registry.persistence.VKey;
import google.registry.persistence.WithVKey;
import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
/**
* A persistable contact resource including mutable and non-mutable fields.
*
* @see <a href="https://tools.ietf.org/html/rfc5733">RFC 5733</a>
*/
@Entity
@Table(
name = "Contact",
indexes = {
@Index(columnList = "creationTime"),
@Index(columnList = "currentSponsorRegistrarId"),
@Index(columnList = "deletionTime"),
@Index(columnList = "contactId"),
@Index(columnList = "searchName")
})
@ExternalMessagingName("contact")
@WithVKey(String.class)
@Access(AccessType.FIELD)
public class Contact extends ContactBase implements ForeignKeyedEppResource {
@Override
public VKey<Contact> createVKey() {
return VKey.create(Contact.class, getRepoId());
}
@Override
@Id
@Access(AccessType.PROPERTY)
public String getRepoId() {
return super.getRepoId();
}
@Override
public Builder asBuilder() {
return new Builder(clone(this));
}
/** A builder for constructing {@link Contact}, since it is immutable. */
public static class Builder extends ContactBase.Builder<Contact, Builder> {
public Builder() {}
private Builder(Contact instance) {
super(instance);
}
public Builder copyFrom(ContactBase contactBase) {
return this.setAuthInfo(contactBase.getAuthInfo())
.setContactId(contactBase.getContactId())
.setCreationRegistrarId(contactBase.getCreationRegistrarId())
.setCreationTime(contactBase.getCreationTime())
.setDeletionTime(contactBase.getDeletionTime())
.setDisclose(contactBase.getDisclose())
.setEmailAddress(contactBase.getEmailAddress())
.setFaxNumber(contactBase.getFaxNumber())
.setInternationalizedPostalInfo(contactBase.getInternationalizedPostalInfo())
.setLastTransferTime(contactBase.getLastTransferTime())
.setLastEppUpdateRegistrarId(contactBase.getLastEppUpdateRegistrarId())
.setLastEppUpdateTime(contactBase.getLastEppUpdateTime())
.setLocalizedPostalInfo(contactBase.getLocalizedPostalInfo())
.setPersistedCurrentSponsorRegistrarId(
contactBase.getPersistedCurrentSponsorRegistrarId())
.setRepoId(contactBase.getRepoId())
.setStatusValues(contactBase.getStatusValues())
.setTransferData(contactBase.getTransferData())
.setVoiceNumber(contactBase.getVoiceNumber());
}
}
}

View File

@@ -1,388 +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.contact;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.collect.ImmutableList;
import google.registry.model.EppResource;
import google.registry.model.EppResource.ResourceWithTransferData;
import google.registry.model.transfer.ContactTransferData;
import google.registry.persistence.VKey;
import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.AttributeOverrides;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Embedded;
import jakarta.persistence.MappedSuperclass;
import jakarta.xml.bind.annotation.XmlElement;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import org.joda.time.DateTime;
/**
* A persistable contact resource including mutable and non-mutable fields.
*
* <p>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
* the DB itself or as part of another entity
*
* @see <a href="https://tools.ietf.org/html/rfc5733">RFC 5733</a>
*/
@MappedSuperclass
@Embeddable
@Access(AccessType.FIELD)
public class ContactBase extends EppResource
implements ResourceWithTransferData<ContactTransferData> {
/**
* Unique identifier for this contact.
*
* <p>This is only unique in the sense that for any given lifetime specified as the time range
* from (creationTime, deletionTime) there can only be one contact in the database with this id.
* However, there can be many contacts with the same id and non-overlapping lifetimes.
*/
String contactId;
/**
* Localized postal info for the contact. All contained values must be representable in the 7-bit
* US-ASCII character set. Personal info; cleared by {@link Contact.Builder#wipeOut}.
*/
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "name", column = @Column(name = "addr_local_name")),
@AttributeOverride(name = "org", column = @Column(name = "addr_local_org")),
@AttributeOverride(name = "type", column = @Column(name = "addr_local_type")),
@AttributeOverride(
name = "address.streetLine1",
column = @Column(name = "addr_local_street_line1")),
@AttributeOverride(
name = "address.streetLine2",
column = @Column(name = "addr_local_street_line2")),
@AttributeOverride(
name = "address.streetLine3",
column = @Column(name = "addr_local_street_line3")),
@AttributeOverride(name = "address.city", column = @Column(name = "addr_local_city")),
@AttributeOverride(name = "address.state", column = @Column(name = "addr_local_state")),
@AttributeOverride(name = "address.zip", column = @Column(name = "addr_local_zip")),
@AttributeOverride(
name = "address.countryCode",
column = @Column(name = "addr_local_country_code"))
})
PostalInfo localizedPostalInfo;
/**
* Internationalized postal info for the contact. Personal info; cleared by {@link
* Contact.Builder#wipeOut}.
*/
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "name", column = @Column(name = "addr_i18n_name")),
@AttributeOverride(name = "org", column = @Column(name = "addr_i18n_org")),
@AttributeOverride(name = "type", column = @Column(name = "addr_i18n_type")),
@AttributeOverride(
name = "address.streetLine1",
column = @Column(name = "addr_i18n_street_line1")),
@AttributeOverride(
name = "address.streetLine2",
column = @Column(name = "addr_i18n_street_line2")),
@AttributeOverride(
name = "address.streetLine3",
column = @Column(name = "addr_i18n_street_line3")),
@AttributeOverride(name = "address.city", column = @Column(name = "addr_i18n_city")),
@AttributeOverride(name = "address.state", column = @Column(name = "addr_i18n_state")),
@AttributeOverride(name = "address.zip", column = @Column(name = "addr_i18n_zip")),
@AttributeOverride(
name = "address.countryCode",
column = @Column(name = "addr_i18n_country_code"))
})
PostalInfo internationalizedPostalInfo;
/**
* Contact name used for name searches. This is set automatically to be the internationalized
* postal name, or if null, the localized postal name, or if that is null as well, null. Personal
* info; cleared by {@link Contact.Builder#wipeOut}.
*/
String searchName;
/** Contacts voice number. Personal info; cleared by {@link Contact.Builder#wipeOut}. */
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "phoneNumber", column = @Column(name = "voice_phone_number")),
@AttributeOverride(name = "extension", column = @Column(name = "voice_phone_extension")),
})
ContactPhoneNumber voice;
/** Contacts fax number. Personal info; cleared by {@link Contact.Builder#wipeOut}. */
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "phoneNumber", column = @Column(name = "fax_phone_number")),
@AttributeOverride(name = "extension", column = @Column(name = "fax_phone_extension")),
})
ContactPhoneNumber fax;
/** Contacts email address. Personal info; cleared by {@link Contact.Builder#wipeOut}. */
String email;
/** Authorization info (aka transfer secret) of the contact. */
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "pw.value", column = @Column(name = "auth_info_value")),
@AttributeOverride(name = "pw.repoId", column = @Column(name = "auth_info_repo_id")),
})
ContactAuthInfo authInfo;
/** Data about any pending or past transfers on this contact. */
ContactTransferData transferData;
/**
* The time that this resource was last transferred.
*
* <p>Can be null if the resource has never been transferred.
*/
DateTime lastTransferTime;
// If any new fields are added which contain personal information, make sure they are cleared by
// the wipeOut() function, so that data is not kept around for deleted contacts.
/** Disclosure policy. */
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "name", column = @Column(name = "disclose_types_name")),
@AttributeOverride(name = "org", column = @Column(name = "disclose_types_org")),
@AttributeOverride(name = "addr", column = @Column(name = "disclose_types_addr")),
@AttributeOverride(name = "flag", column = @Column(name = "disclose_mode_flag")),
@AttributeOverride(name = "voice.marked", column = @Column(name = "disclose_show_voice")),
@AttributeOverride(name = "fax.marked", column = @Column(name = "disclose_show_fax")),
@AttributeOverride(name = "email.marked", column = @Column(name = "disclose_show_email"))
})
Disclose disclose;
@Override
public VKey<? extends ContactBase> createVKey() {
throw new UnsupportedOperationException(
"ContactBase is not an actual persisted entity you can create a key to;"
+ " use Contact instead");
}
public String getContactId() {
return contactId;
}
public PostalInfo getLocalizedPostalInfo() {
return localizedPostalInfo;
}
public PostalInfo getInternationalizedPostalInfo() {
return internationalizedPostalInfo;
}
public String getSearchName() {
return searchName;
}
public ContactPhoneNumber getVoiceNumber() {
return voice;
}
public ContactPhoneNumber getFaxNumber() {
return fax;
}
public String getEmailAddress() {
return email;
}
public ContactAuthInfo getAuthInfo() {
return authInfo;
}
public Disclose getDisclose() {
return disclose;
}
public String getCurrentSponsorRegistrarId() {
return getPersistedCurrentSponsorRegistrarId();
}
@Override
public ContactTransferData getTransferData() {
return Optional.ofNullable(transferData).orElse(ContactTransferData.EMPTY);
}
@Override
public DateTime getLastTransferTime() {
return lastTransferTime;
}
@Override
public String getForeignKey() {
return contactId;
}
/**
* Postal info for the contact.
*
* <p>The XML marshalling expects the {@link PostalInfo} objects in a list, but we can't actually
* persist them directly due to legacy reasons (Objectify can't handle collections of embedded
* objects that themselves contain collections, and there's a list of streets inside). This method
* transforms the persisted format to the XML format for marshalling.
*/
@XmlElement(name = "postalInfo")
public ImmutableList<PostalInfo> getPostalInfosAsList() {
return Stream.of(localizedPostalInfo, internationalizedPostalInfo)
.filter(Objects::nonNull)
.collect(toImmutableList());
}
@Override
public ContactBase cloneProjectedAtTime(DateTime now) {
// Contacts no longer exist and thus do not need to be projected
return this;
}
@Override
public Builder<? extends ContactBase, ?> asBuilder() {
return new Builder<>(clone(this));
}
/** A builder for constructing {@link Contact}, since it is immutable. */
public static class Builder<T extends ContactBase, B extends Builder<T, B>>
extends EppResource.Builder<T, B> implements BuilderWithTransferData<ContactTransferData, B> {
public Builder() {}
protected Builder(T instance) {
super(instance);
}
public B setContactId(String contactId) {
getInstance().contactId = contactId;
return thisCastToDerived();
}
public B setLocalizedPostalInfo(PostalInfo localizedPostalInfo) {
checkArgument(
localizedPostalInfo == null
|| PostalInfo.Type.LOCALIZED.equals(localizedPostalInfo.getType()));
getInstance().localizedPostalInfo = localizedPostalInfo;
return thisCastToDerived();
}
public B setInternationalizedPostalInfo(PostalInfo internationalizedPostalInfo) {
checkArgument(
internationalizedPostalInfo == null
|| PostalInfo.Type.INTERNATIONALIZED.equals(internationalizedPostalInfo.getType()));
getInstance().internationalizedPostalInfo = internationalizedPostalInfo;
return thisCastToDerived();
}
public B overlayLocalizedPostalInfo(PostalInfo localizedPostalInfo) {
return setLocalizedPostalInfo(
getInstance().localizedPostalInfo == null
? localizedPostalInfo
: getInstance().localizedPostalInfo.overlay(localizedPostalInfo));
}
public B overlayInternationalizedPostalInfo(PostalInfo internationalizedPostalInfo) {
return setInternationalizedPostalInfo(
getInstance().internationalizedPostalInfo == null
? internationalizedPostalInfo
: getInstance().internationalizedPostalInfo.overlay(internationalizedPostalInfo));
}
public B setVoiceNumber(ContactPhoneNumber voiceNumber) {
if (voiceNumber != null && voiceNumber.hasNullFields()) {
voiceNumber = null;
}
getInstance().voice = voiceNumber;
return thisCastToDerived();
}
public B setFaxNumber(ContactPhoneNumber faxNumber) {
if (faxNumber != null && faxNumber.hasNullFields()) {
faxNumber = null;
}
getInstance().fax = faxNumber;
return thisCastToDerived();
}
public B setEmailAddress(String emailAddress) {
getInstance().email = emailAddress;
return thisCastToDerived();
}
public B setAuthInfo(ContactAuthInfo authInfo) {
getInstance().authInfo = authInfo;
return thisCastToDerived();
}
public B setDisclose(Disclose disclose) {
getInstance().disclose = disclose;
return thisCastToDerived();
}
@Override
public B setTransferData(ContactTransferData transferData) {
getInstance().transferData = transferData;
return thisCastToDerived();
}
@Override
public B setLastTransferTime(DateTime lastTransferTime) {
getInstance().lastTransferTime = lastTransferTime;
return thisCastToDerived();
}
/**
* Remove all personally identifying information about a contact.
*
* <p>This should be used when deleting a contact so that the soft-deleted entity doesn't
* contain information that the registrant requested to be deleted.
*/
public B wipeOut() {
setEmailAddress(null);
setFaxNumber(null);
setInternationalizedPostalInfo(null);
setLocalizedPostalInfo(null);
setVoiceNumber(null);
return thisCastToDerived();
}
@Override
public T build() {
T instance = getInstance();
// If TransferData is totally empty, set it to null.
if (ContactTransferData.EMPTY.equals(instance.transferData)) {
setTransferData(null);
}
// Set the searchName using the internationalized and localized postal info names.
if ((instance.internationalizedPostalInfo != null)
&& (instance.internationalizedPostalInfo.getName() != null)) {
instance.searchName = instance.internationalizedPostalInfo.getName();
} else if ((instance.localizedPostalInfo != null)
&& (instance.localizedPostalInfo.getName() != null)) {
instance.searchName = instance.localizedPostalInfo.getName();
} else {
instance.searchName = null;
}
return super.build();
}
}
}

View File

@@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkState;
import static google.registry.util.CollectionUtils.nullToEmpty;
import com.google.common.collect.Maps;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.contact.PostalInfo.Type;
import google.registry.model.eppinput.ResourceCommand.AbstractSingleResourceCommand;
@@ -34,13 +35,13 @@ import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.util.List;
import java.util.Map;
/** A collection of {@link Contact} commands. */
/** A collection of (vestigial) Contact commands. */
public class ContactCommand {
/** The fields on "chgType" from <a href="http://tools.ietf.org/html/rfc5733">RFC5733</a>. */
@XmlTransient
public static class ContactCreateOrChange extends ImmutableObject
implements ResourceCreateOrChange<Contact.Builder> {
implements ResourceCreateOrChange<EppResource.Builder<?, ?>> {
/** Postal info for the contact. */
List<PostalInfo> postalInfo;
@@ -111,13 +112,13 @@ public class ContactCommand {
}
/**
* A create command for a {@link Contact}, mapping "createType" from <a
* A create command for a (vestigial) Contact, mapping "createType" from <a
* href="http://tools.ietf.org/html/rfc5733">RFC5733</a>}.
*/
@XmlType(propOrder = {"contactId", "postalInfo", "voice", "fax", "email", "authInfo", "disclose"})
@XmlRootElement
public static class Create extends ContactCreateOrChange
implements SingleResourceCommand, ResourceCreateOrChange<Contact.Builder> {
implements SingleResourceCommand, ResourceCreateOrChange<EppResource.Builder<?, ?>> {
/**
* Unique identifier for this contact.
*
@@ -139,29 +140,29 @@ public class ContactCommand {
}
}
/** A delete command for a {@link Contact}. */
/** A delete command for a (vestigial) Contact. */
@XmlRootElement
public static class Delete extends AbstractSingleResourceCommand {}
/** An info request for a {@link Contact}. */
/** An info request for a (vestigial) Contact. */
@XmlRootElement
@XmlType(propOrder = {"targetId", "authInfo"})
public static class Info extends AbstractContactAuthCommand {}
/** A check request for {@link Contact}. */
/** A check request for (vestigial) Contact. */
@XmlRootElement
public static class Check extends ResourceCheck {}
/** A transfer operation for a {@link Contact}. */
/** A transfer operation for a (vestigial) Contact. */
@XmlRootElement
@XmlType(propOrder = {"targetId", "authInfo"})
public static class Transfer extends AbstractContactAuthCommand {}
/** An update to a {@link Contact}. */
/** An update to a (vestigial) Contact. */
@XmlRootElement
@XmlType(propOrder = {"targetId", "innerAdd", "innerRemove", "innerChange"})
public static class Update
extends ResourceUpdate<Update.AddRemove, Contact.Builder, Update.Change> {
extends ResourceUpdate<Update.AddRemove, EppResource.Builder<?, ?>, Update.Change> {
@XmlElement(name = "chg")
protected Change innerChange;

View File

@@ -1,102 +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.contact;
import google.registry.model.EppResource;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import java.util.Optional;
import javax.annotation.Nullable;
/**
* A persisted history entry representing an EPP modification to a contact.
*
* <p>In addition to the general history fields (e.g. action time, registrar ID) we also persist a
* copy of the contact entity at this point in time. We persist a raw {@link ContactBase} so that
* the foreign-keyed fields in that class can refer to this object.
*/
@Entity
@Table(
indexes = {
@Index(columnList = "creationTime"),
@Index(columnList = "historyRegistrarId"),
@Index(columnList = "historyType"),
@Index(columnList = "historyModificationTime")
})
@AttributeOverride(name = "repoId", column = @Column(name = "contactRepoId"))
@Access(AccessType.FIELD)
public class ContactHistory extends HistoryEntry {
// Store ContactBase instead of Contact, so we don't pick up its @Id
// @Nullable for the sake of pre-Registry-3.0 history objects
@Nullable ContactBase resource;
@Override
protected ContactBase getResource() {
return resource;
}
/**
* The values of all the fields on the {@link ContactBase} object after the action represented by
* this history object was executed.
*
* <p>Will be absent for objects created prior to the Registry 3.0 SQL migration.
*/
public Optional<ContactBase> getContactBase() {
return Optional.ofNullable(resource);
}
/** Creates a {@link VKey} instance for this entity. */
@Override
public VKey<ContactHistory> createVKey() {
return VKey.create(ContactHistory.class, getHistoryEntryId());
}
@Override
public Optional<? extends EppResource> getResourceAtPointInTime() {
return getContactBase().map(contactBase -> new Contact.Builder().copyFrom(contactBase).build());
}
@Override
public Builder asBuilder() {
return new Builder(clone(this));
}
public static class Builder extends HistoryEntry.Builder<ContactHistory, ContactHistory.Builder> {
public Builder() {}
public Builder(ContactHistory instance) {
super(instance);
}
public Builder setContact(ContactBase contactBase) {
getInstance().resource = contactBase;
return setRepoId(contactBase);
}
public Builder wipeOutPii() {
getInstance().resource = getInstance().resource.asBuilder().wipeOut().build();
return this;
}
}
}

View File

@@ -20,11 +20,9 @@ import jakarta.persistence.Embeddable;
/**
* EPP Contact Phone Number
*
* <p>This class is embedded inside a {@link Contact} hold the phone number of an EPP contact. The
* fields are all defined in the parent class {@link PhoneNumber}, but the subclass is still
* necessary to pick up the contact namespace.
*
* @see Contact
* <p>This class is embedded inside a (vestigial) Contact to hold the phone number of an EPP
* contact. The fields are all defined in the parent class {@link PhoneNumber}, but the subclass is
* still necessary to pick up the contact namespace.
*/
@Embeddable
public class ContactPhoneNumber extends PhoneNumber {

View File

@@ -1,80 +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.domain;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import google.registry.model.contact.Contact;
import google.registry.persistence.VKey;
import jakarta.persistence.Embeddable;
import jakarta.xml.bind.annotation.XmlEnumValue;
/**
* Persisted type for storing a domain's contact associations.
*
* <p>A contact association on a domain consists of the contact key and the contact "type", which is
* the designated role of this contact with respect to this domain. When converting to and from EPP
* XML, we use {@link ForeignKeyedDesignatedContact} to replace the contact's primary key with its
* foreign key, since that is what EPP exposes.
*
* <p>Note one could in principle store contact foreign keys here in addition to keys, unlike the
* situation with hosts where client-side renames would make that data stale. However, we sometimes
* rename contacts internally ourselves, and it's easier to use the same model for both cases.
*
* <p>This entity type is not persisted in Cloud SQL. The different roles are represented as
* separate fields in the Domain table.
*
* @see <a href="http://tools.ietf.org/html/rfc5731#section-2.2">RFC 5731 - EPP Domain Name Mapping
* - Contact and Client Identifiers</a>
*/
@Embeddable
public class DesignatedContact extends ImmutableObject implements UnsafeSerializable {
/**
* XML type for contact types. This can be either: {@code "admin"}, {@code "billing"}, or
* {@code "tech"} and corresponds to {@code contactAttrType} in {@code domain-1.0.xsd}.
*/
public enum Type {
@XmlEnumValue("admin")
ADMIN,
@XmlEnumValue("billing")
BILLING,
@XmlEnumValue("tech")
TECH,
/** The registrant type is not reflected in XML and exists only for internal use. */
REGISTRANT
}
public static DesignatedContact create(Type type, VKey<Contact> contact) {
DesignatedContact instance = new DesignatedContact();
instance.type = type;
instance.contactVKey = checkArgumentNotNull(contact, "Must specify contact key");
return instance;
}
Type type;
VKey<Contact> contactVKey;
public Type getType() {
return type;
}
public VKey<Contact> getContactKey() {
return contactVKey;
}
}

View File

@@ -178,7 +178,6 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
.setAutorenewPollMessage(domainBase.getAutorenewPollMessage())
.setAutorenewBillingEvent(domainBase.getAutorenewBillingEvent())
.setAutorenewEndTime(domainBase.getAutorenewEndTime())
.setContacts(domainBase.getContacts())
.setCreationRegistrarId(domainBase.getCreationRegistrarId())
.setCreationTime(domainBase.getCreationTime())
.setDomainName(domainBase.getDomainName())
@@ -193,7 +192,6 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
.setLastEppUpdateTime(domainBase.getLastEppUpdateTime())
.setNameservers(domainBase.getNameservers())
.setPersistedCurrentSponsorRegistrarId(domainBase.getPersistedCurrentSponsorRegistrarId())
.setRegistrant(domainBase.getRegistrant())
.setRegistrationExpirationTime(domainBase.getRegistrationExpirationTime())
.setRepoId(domainBase.getRepoId())
.setSmdId(domainBase.getSmdId())

View File

@@ -45,8 +45,6 @@ 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.contact.Contact;
import google.registry.model.domain.DesignatedContact.Type;
import google.registry.model.domain.launch.LaunchNotice;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.domain.secdns.DomainDsData;
@@ -79,10 +77,8 @@ import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.Transient;
import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import org.hibernate.collection.spi.PersistentSet;
import org.joda.time.DateTime;
@@ -131,12 +127,12 @@ public class DomainBase extends EppResource
/** References to hosts that are the nameservers for the domain. */
@Expose @Transient Set<VKey<Host>> nsHosts;
/** Contacts. */
@Expose @Nullable VKey<Contact> adminContact;
/** Contacts keys are kept around for vestigial purposes for now. */
@Expose @Nullable String adminContact;
@Expose @Nullable VKey<Contact> billingContact;
@Expose @Nullable VKey<Contact> techContact;
@Expose @Nullable VKey<Contact> registrantContact;
@Expose @Nullable String billingContact;
@Expose @Nullable String techContact;
@Expose @Nullable String registrantContact;
/** Authorization info (aka transfer secret) of the domain. */
@Embedded
@@ -586,120 +582,21 @@ public class DomainBase extends EppResource
.collect(toImmutableSortedSet(Ordering.natural())));
}
/** A key to the registrant who registered this domain. */
public Optional<VKey<Contact>> getRegistrant() {
return Optional.ofNullable(registrantContact);
}
public Optional<VKey<Contact>> getAdminContact() {
return Optional.ofNullable(adminContact);
}
public Optional<VKey<Contact>> getBillingContact() {
return Optional.ofNullable(billingContact);
}
public Optional<VKey<Contact>> getTechContact() {
return Optional.ofNullable(techContact);
}
/**
* Associated contacts for the domain (other than registrant).
*
* <p>Note: This can be an empty set if no contacts are present for the domain.
*/
public ImmutableSet<DesignatedContact> getContacts() {
return getAllContacts(false);
}
/**
* Gets all associated contacts for the domain, including the registrant.
*
* <p>Note: This can be an empty set if no contacts are present for the domain.
*/
public ImmutableSet<DesignatedContact> getAllContacts() {
return getAllContacts(true);
}
@Nullable
public DomainAuthInfo getAuthInfo() {
return authInfo;
}
/**
* Returns all referenced contacts from this domain.
*
* <p>Note: This can be an empty set if no contacts are present for the domain.
*/
public ImmutableSet<VKey<Contact>> getReferencedContacts() {
return nullToEmptyImmutableCopy(getAllContacts(true)).stream()
.map(DesignatedContact::getContactKey)
.filter(Objects::nonNull)
.collect(toImmutableSet());
}
private ImmutableSet<DesignatedContact> getAllContacts(boolean includeRegistrant) {
ImmutableSet.Builder<DesignatedContact> builder = new ImmutableSet.Builder<>();
if (includeRegistrant) {
getRegistrant().ifPresent(c -> builder.add(DesignatedContact.create(Type.REGISTRANT, c)));
}
getAdminContact().ifPresent(c -> builder.add(DesignatedContact.create(Type.ADMIN, c)));
getBillingContact().ifPresent(c -> builder.add(DesignatedContact.create(Type.BILLING, c)));
getTechContact().ifPresent(c -> builder.add(DesignatedContact.create(Type.TECH, c)));
return builder.build();
}
public String getTld() {
return tld;
}
/**
* Sets the individual contact fields from {@code contacts}.
*
* <p>The registrant field is only set if {@code includeRegistrant} is true, as this field needs
* to be set in some circumstances but not in others.
*/
void setContactFields(Set<DesignatedContact> contacts, boolean includeRegistrant) {
// Set the individual contact fields.
billingContact = null;
techContact = null;
adminContact = null;
if (includeRegistrant) {
registrantContact = null;
}
HashSet<Type> contactsDiscovered = new HashSet<>();
for (DesignatedContact contact : contacts) {
checkArgument(
!contactsDiscovered.contains(contact.getType()),
"Duplicate contact type %s in designated contact set.",
contact.getType());
contactsDiscovered.add(contact.getType());
switch (contact.getType()) {
case BILLING -> billingContact = contact.getContactKey();
case TECH -> techContact = contact.getContactKey();
case ADMIN -> adminContact = contact.getContactKey();
case REGISTRANT -> {
if (includeRegistrant) {
registrantContact = contact.getContactKey();
}
}
default ->
throw new IllegalArgumentException(
"Unknown contact resource type: " + contact.getType());
}
}
}
@Override
public VKey<Domain> createVKey() {
throw new UnsupportedOperationException(
"DomainBase is not an actual persisted entity you can create a key to; use Domain instead");
}
/** Predicate to determine if a given {@link DesignatedContact} is the registrant. */
static final Predicate<DesignatedContact> IS_REGISTRANT =
(DesignatedContact contact) -> Type.REGISTRANT.equals(contact.type);
/** An override of {@link EppResource#asBuilder} with tighter typing. */
@Override
public Builder<? extends DomainBase, ?> asBuilder() {
@@ -764,12 +661,6 @@ public class DomainBase extends EppResource
return thisCastToDerived();
}
public B setRegistrant(Optional<VKey<Contact>> registrant) {
// Set the registrant field specifically.
getInstance().registrantContact = registrant.orElse(null);
return thisCastToDerived();
}
public B setAuthInfo(DomainAuthInfo authInfo) {
getInstance().authInfo = authInfo;
return thisCastToDerived();
@@ -805,26 +696,6 @@ public class DomainBase extends EppResource
ImmutableSet.copyOf(difference(getInstance().getNameservers(), nameservers)));
}
public B setContacts(DesignatedContact contact) {
return setContacts(ImmutableSet.of(contact));
}
public B setContacts(ImmutableSet<DesignatedContact> contacts) {
checkArgument(contacts.stream().noneMatch(IS_REGISTRANT), "Registrant cannot be a contact");
// Set the individual fields.
getInstance().setContactFields(contacts, false);
return thisCastToDerived();
}
public B addContacts(ImmutableSet<DesignatedContact> contacts) {
return setContacts(ImmutableSet.copyOf(Sets.union(getInstance().getContacts(), contacts)));
}
public B removeContacts(ImmutableSet<DesignatedContact> contacts) {
return setContacts(ImmutableSet.copyOf(difference(getInstance().getContacts(), contacts)));
}
public B setLaunchNotice(LaunchNotice launchNotice) {
getInstance().launchNotice = launchNotice;
return thisCastToDerived();

View File

@@ -30,7 +30,6 @@ import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedExceptio
import google.registry.flows.exceptions.ContactsProhibitedException;
import google.registry.model.ForeignKeyUtils;
import google.registry.model.ImmutableObject;
import google.registry.model.contact.Contact;
import google.registry.model.eppinput.ResourceCommand.AbstractSingleResourceCommand;
import google.registry.model.eppinput.ResourceCommand.ResourceCheck;
import google.registry.model.eppinput.ResourceCommand.ResourceCreateOrChange;
@@ -79,9 +78,6 @@ public class DomainCommand {
@Nullable
String registrantContactId;
/** A resolved key to the registrant who registered this domain. */
@Nullable @XmlTransient VKey<Contact> registrant;
/** Authorization info (aka transfer secret) of the domain. */
DomainAuthInfo authInfo;
@@ -89,10 +85,6 @@ public class DomainCommand {
return Optional.ofNullable(registrantContactId);
}
public Optional<VKey<Contact>> getRegistrant() {
return Optional.ofNullable(registrant);
}
public DomainAuthInfo getAuthInfo() {
return authInfo;
}
@@ -131,10 +123,6 @@ public class DomainCommand {
@XmlElement(name = "contact")
Set<ForeignKeyedDesignatedContact> foreignKeyedDesignatedContacts;
/** Resolved keys to associated contacts for the domain (other than registrant). */
@XmlTransient
Set<DesignatedContact> contacts;
/** The period that this domain's state was set to last for (e.g. 1-10 years). */
Period period;
@@ -159,10 +147,6 @@ public class DomainCommand {
return nullToEmptyImmutableCopy(nameservers);
}
public ImmutableSet<DesignatedContact> getContacts() {
return nullToEmptyImmutableCopy(contacts);
}
@Override
public DomainAuthInfo getAuthInfo() {
return authInfo;
@@ -340,10 +324,6 @@ public class DomainCommand {
@XmlElement(name = "contact")
Set<ForeignKeyedDesignatedContact> foreignKeyedDesignatedContacts;
/** Resolved keys to associated contacts for the domain (other than registrant). */
@XmlTransient
Set<DesignatedContact> contacts;
public ImmutableSet<String> getNameserverHostNames() {
return nullSafeImmutableCopy(nameserverHostNames);
}
@@ -352,10 +332,6 @@ public class DomainCommand {
return nullToEmptyImmutableCopy(nameservers);
}
public ImmutableSet<DesignatedContact> getContacts() {
return nullToEmptyImmutableCopy(contacts);
}
/** Creates a copy of this {@link AddRemove} with hard links to hosts and contacts. */
private AddRemove cloneAndLinkReferences(DateTime now)
throws InvalidReferencesException, ContactsProhibitedException {

View File

@@ -16,27 +16,42 @@ package google.registry.model.domain;
import google.registry.model.ImmutableObject;
import jakarta.xml.bind.annotation.XmlAttribute;
import jakarta.xml.bind.annotation.XmlEnumValue;
import jakarta.xml.bind.annotation.XmlValue;
/**
* EPP-XML-serializable equivalent of {@link DesignatedContact}.
* Vestigial EPP-XML-serializable equivalent of a contact.
*
* <p>This type is used on the wire for EPP XML, where only the contact ID (foreign key) is exposed.
* This is converted to and from the persisted type, {@link DesignatedContact}, which stores the
* primary key instead of the foreign key.
* <p>This type was used on the wire for EPP XML, where only the contact ID (foreign key) was
* exposed.
*
* @see <a href="http://tools.ietf.org/html/rfc5731#section-2.2">RFC 5731 - EPP Domain Name Mapping
* - Contact and Client Identifiers</a>
*/
public class ForeignKeyedDesignatedContact extends ImmutableObject {
/**
* XML type for contact types. This can be either: {@code "admin"}, {@code "billing"}, or {@code
* "tech"} and corresponds to {@code contactAttrType} in {@code domain-1.0.xsd}.
*/
public enum Type {
@XmlEnumValue("admin")
ADMIN,
@XmlEnumValue("billing")
BILLING,
@XmlEnumValue("tech")
TECH,
/** The registrant type is not reflected in XML and exists only for internal use. */
REGISTRANT
}
@XmlAttribute(required = true)
DesignatedContact.Type type;
Type type;
@XmlValue
String contactId;
public static ForeignKeyedDesignatedContact create(
DesignatedContact.Type type, String contactId) {
public static ForeignKeyedDesignatedContact create(Type type, String contactId) {
ForeignKeyedDesignatedContact instance = new ForeignKeyedDesignatedContact();
instance.type = type;
instance.contactId = contactId;

View File

@@ -22,8 +22,6 @@ import com.google.common.collect.ImmutableSet;
import google.registry.model.EppResource;
import google.registry.model.adapters.EnumToAttributeAdapter.EppEnum;
import google.registry.model.adapters.StatusValueAdapter;
import google.registry.model.contact.Contact;
import google.registry.model.contact.ContactBase;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainBase;
import google.registry.model.host.Host;
@@ -130,8 +128,6 @@ public enum StatusValue implements EppEnum {
/** Enum to help clearly list which resource types a status value is allowed to be present on. */
private enum AllowedOn {
ALL(
Contact.class,
ContactBase.class,
Domain.class,
DomainBase.class,
Host.class,

View File

@@ -27,7 +27,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.gson.annotations.Expose;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain;
import google.registry.model.host.Host;
import google.registry.util.SerializeUtils;
@@ -51,7 +50,7 @@ public class VKey<T> extends ImmutableObject implements Serializable {
private static final String DELIMITER = "@";
private static final ImmutableMap<String, Class<? extends EppResource>> EPP_RESOURCE_CLASS_MAP =
ImmutableList.of(Domain.class, Host.class, Contact.class).stream()
ImmutableList.of(Domain.class, Host.class).stream()
.collect(toImmutableMap(Class::getSimpleName, identity()));
// The primary key for the referenced entity.

View File

@@ -44,7 +44,6 @@ import google.registry.gcs.GcsUtils;
import google.registry.keyring.api.KeyModule.Key;
import google.registry.model.common.Cursor;
import google.registry.model.common.Cursor.CursorType;
import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain;
import google.registry.model.host.Host;
import google.registry.model.rde.RdeMode;
@@ -80,8 +79,8 @@ import org.joda.time.Duration;
* type and loads the embedded resource from it, which is then projected to watermark time to
* account for things like pending transfer.
*
* <p>Only {@link Contact}s and {@link Host}s that are referenced by an included {@link Domain} will
* be included in the corresponding pending deposit.
* <p>Only {@link Host}s that are referenced by an included {@link Domain} will be included in the
* corresponding pending deposit.
*
* <p>{@link Registrar} entities, both active and inactive, are included in all deposits. They are
* not rewound point-in-time.

View File

@@ -19,7 +19,6 @@ import static com.google.common.base.Preconditions.checkState;
import com.google.common.base.Ascii;
import google.registry.model.EppResource;
import google.registry.model.ForeignKeyUtils;
import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain;
import google.registry.model.host.Host;
import google.registry.persistence.VKey;
@@ -30,7 +29,6 @@ class CommandUtilities {
/** A useful parameter enum for commands that operate on {@link EppResource} objects. */
public enum ResourceType {
CONTACT(Contact.class),
HOST(Host.class),
DOMAIN(Domain.class);

View File

@@ -51,8 +51,6 @@
<class>google.registry.model.console.ConsoleUpdateHistory</class>
<class>google.registry.model.console.PasswordResetRequest</class>
<class>google.registry.model.console.User</class>
<class>google.registry.model.contact.ContactHistory</class>
<class>google.registry.model.contact.Contact</class>
<class>google.registry.model.domain.Domain</class>
<class>google.registry.model.domain.DomainHistory</class>
<class>google.registry.model.domain.GracePeriod</class>
@@ -94,7 +92,6 @@
<class>google.registry.model.billing.VKeyConverter_BillingCancellation</class>
<class>google.registry.model.billing.VKeyConverter_BillingEvent</class>
<class>google.registry.model.billing.VKeyConverter_BillingRecurrence</class>
<class>google.registry.model.contact.VKeyConverter_Contact</class>
<class>google.registry.model.domain.VKeyConverter_Domain</class>
<class>google.registry.model.domain.token.VKeyConverter_AllocationToken</class>
<class>google.registry.model.host.VKeyConverter_Host</class>

View File

@@ -185,7 +185,6 @@ public class RegistryJpaReadTest {
StatusValue.SERVER_UPDATE_PROHIBITED,
StatusValue.SERVER_RENEW_PROHIBITED,
StatusValue.SERVER_HOLD))
.setContacts(ImmutableSet.of())
.setSubordinateHosts(ImmutableSet.of("ns1.example.com"))
.setPersistedCurrentSponsorRegistrarId(registrar.getRegistrarId())
.setRegistrationExpirationTime(fakeClock.nowUtc().plusYears(1))

View File

@@ -81,7 +81,6 @@ import google.registry.testing.CloudTasksHelper.TaskMatcher;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeKeyringModule;
import java.io.IOException;
import java.util.Optional;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -239,8 +238,6 @@ public class RdePipelineTest {
newDomain("hello.soy")
.asBuilder()
.addNameserver(host1.createVKey())
.setRegistrant(Optional.empty())
.setContacts(ImmutableSet.of())
.build());
persistDomainHistory(helloDomain);
persistHostHistory(persistActiveHost("not-used-subordinate.hello.soy"));
@@ -253,8 +250,6 @@ public class RdePipelineTest {
newDomain("kitty.fun")
.asBuilder()
.addNameservers(ImmutableSet.of(host1.createVKey(), host2.createVKey()))
.setRegistrant(Optional.empty())
.setContacts(ImmutableSet.of())
.build());
persistDomainHistory(kittyDomain);
// Should not appear because the TLD is not included in a pending deposit.

View File

@@ -50,7 +50,6 @@ import static google.registry.testing.DatabaseHelper.getHistoryEntries;
import static google.registry.testing.DatabaseHelper.loadAllOf;
import static google.registry.testing.DatabaseHelper.loadRegistrar;
import static google.registry.testing.DatabaseHelper.newHost;
import static google.registry.testing.DatabaseHelper.persistActiveContact;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistActiveHost;
import static google.registry.testing.DatabaseHelper.persistReservedList;
@@ -1869,7 +1868,6 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
@Test
void testFailure_minimumDataset_noRegistrantButSomeOtherContactTypes() throws Exception {
persistActiveContact("sh8013");
setEppInput("domain_create_other_contact_types.xml");
persistHosts();
EppException thrown = assertThrows(ContactsProhibitedException.class, this::runFlow);
@@ -1878,7 +1876,6 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
@Test
void testFailure_minimumDataset_registrantNotPermitted() throws Exception {
persistActiveContact("jd1234");
setEppInput("domain_create_has_registrant_contact.xml");
persistHosts();
EppException thrown = assertThrows(RegistrantProhibitedException.class, this::runFlow);

View File

@@ -73,7 +73,6 @@ import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTransactionManagerExtension;
import google.registry.testing.DatabaseHelper;
import google.registry.xml.ValidationMode;
import java.util.Optional;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.joda.money.Money;
@@ -214,23 +213,9 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
doSuccessfulTest("domain_info_response.xml");
}
@Test
void testSuccess_noRegistrant() throws Exception {
persistTestEntities(false);
domain = persistResource(domain.asBuilder().setRegistrant(Optional.empty()).build());
doSuccessfulTest("domain_info_response_no_registrant.xml", false);
}
@Test
void testSuccess_noContacts() throws Exception {
persistTestEntities(false);
domain =
persistResource(
domain
.asBuilder()
.setRegistrant(Optional.empty())
.setContacts(ImmutableSet.of())
.build());
doSuccessfulTest("domain_info_response_no_contacts.xml", false);
}

View File

@@ -42,7 +42,6 @@ import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType;
import static google.registry.testing.DatabaseHelper.getPollMessages;
import static google.registry.testing.DatabaseHelper.loadRegistrar;
import static google.registry.testing.DatabaseHelper.persistActiveContact;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistActiveHost;
import static google.registry.testing.DatabaseHelper.persistActiveSubordinateHost;
@@ -272,7 +271,6 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
@Test
void testFailure_minimumDataset_whenAddingNewContacts() throws Exception {
persistActiveContact("mak21");
// This EPP adds a new technical contact mak21 that wasn't already present.
setEppInput("domain_update_empty_registrant.xml");
persistReferencedEntities();
@@ -1375,13 +1373,8 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
@Test
void testFailure_minimumDataset_addingNewRegistrantFails() throws Exception {
persistActiveContact("sh8013");
persistReferencedEntities();
persistResource(
DatabaseHelper.newDomain(getUniqueIdFromCommand())
.asBuilder()
.setRegistrant(Optional.empty())
.build());
persistResource(DatabaseHelper.newDomain(getUniqueIdFromCommand()));
// This EPP sets the registrant to sh8013, whereas in our test setup it is absent.
setEppInput("domain_update_registrant.xml");
RegistrantProhibitedException thrown =

View File

@@ -1,227 +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.contact;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.testing.DatabaseHelper.cloneAndSetAutoTimestamps;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.loadByEntity;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.SqlHelper.assertThrowForeignKeyViolation;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.model.EntityTestCase;
import google.registry.model.ForeignKeyUtils;
import google.registry.model.contact.Disclose.PostalInfoChoice;
import google.registry.model.contact.PostalInfo.Type;
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.eppcommon.PresenceMarker;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid;
import google.registry.model.transfer.ContactTransferData;
import google.registry.model.transfer.TransferStatus;
import google.registry.util.SerializeUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link Contact}. */
public class ContactTest extends EntityTestCase {
private Contact originalContact;
private Contact contact;
ContactTest() {
super(JpaEntityCoverageCheck.ENABLED);
}
@BeforeEach
void setUp() {
createTld("foobar");
originalContact =
new Contact.Builder()
.setContactId("contact_id")
.setRepoId("1-FOOBAR")
.setCreationRegistrarId("TheRegistrar")
.setLastEppUpdateTime(fakeClock.nowUtc())
.setLastEppUpdateRegistrarId("NewRegistrar")
.setLastTransferTime(fakeClock.nowUtc())
.setPersistedCurrentSponsorRegistrarId("NewRegistrar")
.setLocalizedPostalInfo(
new PostalInfo.Builder()
.setType(Type.LOCALIZED)
.setAddress(
new ContactAddress.Builder()
.setStreet(ImmutableList.of("111 8th Ave", "4th Floor"))
.setCity("New York")
.setState("NY")
.setZip("10011")
.setCountryCode("US")
.build())
.build())
.setInternationalizedPostalInfo(
new PostalInfo.Builder()
.setType(Type.INTERNATIONALIZED)
.setAddress(
new ContactAddress.Builder()
.setStreet(ImmutableList.of("111 8th Ave", "4th Floor"))
.setCity("New York")
.setState("NY")
.setZip("10011")
.setCountryCode("US")
.build())
.build())
.setVoiceNumber(new ContactPhoneNumber.Builder().setPhoneNumber("867-5309").build())
.setFaxNumber(
new ContactPhoneNumber.Builder()
.setPhoneNumber("867-5309")
.setExtension("1000")
.build())
.setEmailAddress("jenny@example.com")
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("passw0rd")))
.setDisclose(
new Disclose.Builder()
.setVoice(new PresenceMarker())
.setEmail(new PresenceMarker())
.setFax(new PresenceMarker())
.setFlag(true)
.setAddrs(ImmutableList.of(PostalInfoChoice.create(Type.INTERNATIONALIZED)))
.setNames(ImmutableList.of(PostalInfoChoice.create(Type.INTERNATIONALIZED)))
.setOrgs(ImmutableList.of(PostalInfoChoice.create(Type.INTERNATIONALIZED)))
.build())
.setStatusValues(ImmutableSet.of(StatusValue.OK))
.setTransferData(
new ContactTransferData.Builder()
.setGainingRegistrarId("TheRegistrar")
.setLosingRegistrarId("NewRegistrar")
.setPendingTransferExpirationTime(fakeClock.nowUtc())
.setTransferRequestTime(fakeClock.nowUtc())
.setTransferStatus(TransferStatus.SERVER_APPROVED)
.setTransferRequestTrid(Trid.create("client-trid", "server-trid"))
.build())
.build();
// Set up a new persisted Contact entity.
contact = persistResource(cloneAndSetAutoTimestamps(originalContact));
}
@Test
void testContactBaseToContact() {
assertAboutImmutableObjects()
.that(new Contact.Builder().copyFrom(contact).build())
.isEqualExceptFields(contact, "updateTimestamp", "revisions");
}
@Test
void testCloudSqlPersistence_failWhenViolateForeignKeyConstraint() {
assertThrowForeignKeyViolation(
() ->
persistResource(
originalContact
.asBuilder()
.setRepoId("2-FOOBAR")
.setCreationRegistrarId("nonexistent-registrar")
.build()));
}
@Test
void testCloudSqlPersistence_succeed() {
Contact persisted = loadByEntity(originalContact);
Contact fixed =
originalContact
.asBuilder()
.setCreationTime(persisted.getCreationTime())
.setTransferData(
originalContact
.getTransferData()
.asBuilder()
.setServerApproveEntities(null, null, null)
.build())
.build();
assertAboutImmutableObjects().that(persisted).isEqualExceptFields(fixed, "updateTimestamp");
}
@Test
void testPersistence() {
assertThat(
ForeignKeyUtils.loadResource(
Contact.class, contact.getForeignKey(), fakeClock.nowUtc()))
.hasValue(contact);
}
@Test
void testSerializable() {
Contact persisted =
ForeignKeyUtils.loadResource(Contact.class, contact.getForeignKey(), fakeClock.nowUtc())
.get();
assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
}
@Test
void testEmptyStringsBecomeNull() {
assertThat(new Contact.Builder().setContactId(null).build().getContactId()).isNull();
assertThat(new Contact.Builder().setContactId("").build().getContactId()).isNull();
assertThat(new Contact.Builder().setContactId(" ").build().getContactId()).isNotNull();
// Nested ImmutableObjects should also be fixed
assertThat(
new Contact.Builder()
.setInternationalizedPostalInfo(
new PostalInfo.Builder().setType(Type.INTERNATIONALIZED).setName(null).build())
.build()
.getInternationalizedPostalInfo()
.getName())
.isNull();
assertThat(
new Contact.Builder()
.setInternationalizedPostalInfo(
new PostalInfo.Builder().setType(Type.INTERNATIONALIZED).setName("").build())
.build()
.getInternationalizedPostalInfo()
.getName())
.isNull();
assertThat(
new Contact.Builder()
.setInternationalizedPostalInfo(
new PostalInfo.Builder().setType(Type.INTERNATIONALIZED).setName(" ").build())
.build()
.getInternationalizedPostalInfo()
.getName())
.isNotNull();
}
@Test
void testEmptyTransferDataBecomesNull() {
Contact withNull = new Contact.Builder().setTransferData(null).build();
Contact withEmpty = withNull.asBuilder().setTransferData(ContactTransferData.EMPTY).build();
assertThat(withNull).isEqualTo(withEmpty);
assertThat(withEmpty.transferData).isNull();
}
@Test
void testSetCreationTime_cantBeCalledTwice() {
IllegalStateException thrown =
assertThrows(
IllegalStateException.class, () -> contact.asBuilder().setCreationTime(END_OF_TIME));
assertThat(thrown).hasMessageThat().contains("creationTime can only be set once");
}
@Test
void testToHydratedString_notCircular() {
// If there are circular references, this will overflow the stack.
contact.toHydratedString();
}
}

View File

@@ -236,17 +236,6 @@ public class DomainTest {
.hasValue(domain);
}
@Test
void testRegistrantNotRequired() {
persistResource(domain.asBuilder().setRegistrant(Optional.empty()).build());
String foreignKey = domain.getForeignKey();
assertThat(
ForeignKeyUtils.loadResource(Domain.class, foreignKey, fakeClock.nowUtc())
.get()
.getRegistrant())
.isEmpty();
}
@Test
void testEmptyStringsBecomeNull() {
assertThat(

View File

@@ -19,7 +19,6 @@ import static google.registry.model.eppcommon.EppXmlTransformer.unmarshal;
import static google.registry.testing.TestDataHelper.loadBytes;
import static org.junit.jupiter.api.Assertions.assertThrows;
import google.registry.model.contact.ContactTest;
import google.registry.model.domain.DomainTest;
import google.registry.model.eppinput.EppInput.InnerCommand;
import google.registry.model.eppinput.EppInput.Login;
@@ -29,17 +28,6 @@ import org.junit.jupiter.api.Test;
/** Unit tests for {@link EppInput}. */
class EppInputTest {
@Test
void testUnmarshalling_contactInfo() throws Exception {
EppInput input =
unmarshal(EppInput.class, loadBytes(ContactTest.class, "contact_info.xml").read());
assertThat(input.getCommandWrapper().getClTrid()).hasValue("ABC-12345");
assertThat(input.getCommandType()).isEqualTo("info");
assertThat(input.getResourceType()).hasValue("contact");
assertThat(input.getSingleTargetId()).hasValue("sh8013");
assertThat(input.getTargetIds()).containsExactly("sh8013");
}
@Test
void testUnmarshalling_domainCheck() throws Exception {
EppInput input =

View File

@@ -1,145 +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.history;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.loadByEntity;
import static google.registry.testing.DatabaseHelper.newContactWithRoid;
import static google.registry.testing.DatabaseHelper.persistResource;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
import google.registry.model.EntityTestCase;
import google.registry.model.contact.Contact;
import google.registry.model.contact.ContactAddress;
import google.registry.model.contact.ContactBase;
import google.registry.model.contact.ContactHistory;
import google.registry.model.contact.ContactPhoneNumber;
import google.registry.model.contact.PostalInfo;
import google.registry.model.eppcommon.Trid;
import google.registry.model.reporting.HistoryEntry;
import google.registry.util.SerializeUtils;
import org.junit.jupiter.api.Test;
/** Tests for {@link ContactHistory}. */
public class ContactHistoryTest extends EntityTestCase {
ContactHistoryTest() {
super(JpaEntityCoverageCheck.ENABLED);
}
@Test
void testPersistence() {
Contact contact = newContactWithRoid("contactId", "contact1");
persistResource(contact);
Contact contactFromDb = loadByEntity(contact);
ContactHistory contactHistory = createContactHistory(contactFromDb);
persistResource(contactHistory);
tm().transact(
() -> {
ContactHistory fromDatabase = tm().loadByKey(contactHistory.createVKey());
assertContactHistoriesEqual(fromDatabase, contactHistory);
assertThat(fromDatabase.getRepoId()).isEqualTo(contactHistory.getRepoId());
});
}
@Test
void testSerializable() {
Contact contact = newContactWithRoid("contactId", "contact1");
persistResource(contact);
Contact contactFromDb = loadByEntity(contact);
ContactHistory contactHistory = createContactHistory(contactFromDb);
persistResource(contactHistory);
ContactHistory fromDatabase = tm().transact(() -> tm().loadByKey(contactHistory.createVKey()));
assertThat(SerializeUtils.serializeDeserialize(fromDatabase)).isEqualTo(fromDatabase);
}
@Test
void testWipeOutPii_assertsAllPiiFieldsAreNull() {
ContactHistory originalEntity =
createContactHistory(
new Contact.Builder()
.setRepoId("1-FOOBAR")
.setLocalizedPostalInfo(
new PostalInfo.Builder()
.setType(PostalInfo.Type.LOCALIZED)
.setAddress(
new ContactAddress.Builder()
.setStreet(ImmutableList.of("111 8th Ave", "4th Floor"))
.setCity("New York")
.setState("NY")
.setZip("10011")
.setCountryCode("US")
.build())
.build())
.setInternationalizedPostalInfo(
new PostalInfo.Builder()
.setType(PostalInfo.Type.INTERNATIONALIZED)
.setAddress(
new ContactAddress.Builder()
.setStreet(ImmutableList.of("111 8th Ave", "4th Floor"))
.setCity("New York")
.setState("NY")
.setZip("10011")
.setCountryCode("US")
.build())
.build())
.setVoiceNumber(new ContactPhoneNumber.Builder().setPhoneNumber("867-5309").build())
.setFaxNumber(
new ContactPhoneNumber.Builder()
.setPhoneNumber("867-5309")
.setExtension("1000")
.build())
.setEmailAddress("test@example.com")
.build());
assertThat(originalEntity.getContactBase().get().getEmailAddress()).isNotNull();
assertThat(originalEntity.getContactBase().get().getLocalizedPostalInfo()).isNotNull();
assertThat(originalEntity.getContactBase().get().getInternationalizedPostalInfo()).isNotNull();
assertThat(originalEntity.getContactBase().get().getVoiceNumber()).isNotNull();
assertThat(originalEntity.getContactBase().get().getFaxNumber()).isNotNull();
ContactHistory wipedEntity = originalEntity.asBuilder().wipeOutPii().build();
assertThat(wipedEntity.getContactBase().get().getEmailAddress()).isNull();
assertThat(wipedEntity.getContactBase().get().getLocalizedPostalInfo()).isNull();
assertThat(wipedEntity.getContactBase().get().getInternationalizedPostalInfo()).isNull();
assertThat(wipedEntity.getContactBase().get().getVoiceNumber()).isNull();
assertThat(wipedEntity.getContactBase().get().getFaxNumber()).isNull();
}
private ContactHistory createContactHistory(ContactBase contact) {
return new ContactHistory.Builder()
.setType(HistoryEntry.Type.HOST_CREATE)
.setXmlBytes("<xml></xml>".getBytes(UTF_8))
.setModificationTime(fakeClock.nowUtc())
.setRegistrarId("TheRegistrar")
.setTrid(Trid.create("ABC-123", "server-trid"))
.setBySuperuser(false)
.setReason("reason")
.setRequestedByRegistrar(true)
.setContact(contact)
.build();
}
static void assertContactHistoriesEqual(ContactHistory one, ContactHistory two) {
assertAboutImmutableObjects().that(one).isEqualExceptFields(two, "resource");
assertAboutImmutableObjects()
.that(one.getContactBase().get())
.isEqualExceptFields(two.getContactBase().get());
}
}

View File

@@ -14,11 +14,9 @@
package google.registry.rde;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistEppResource;
import static google.registry.testing.DatabaseHelper.persistResource;
@@ -34,7 +32,6 @@ import google.registry.model.billing.BillingBase.Flag;
import google.registry.model.billing.BillingBase.Reason;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingRecurrence;
import google.registry.model.domain.DesignatedContact;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainAuthInfo;
import google.registry.model.domain.DomainHistory;
@@ -198,12 +195,6 @@ public class DomainToXjcConverterTest {
@Test
void testConvertAbsentContacts() throws XmlException {
Domain domain = makeDomain(clock);
tm().transact(
() ->
tm().delete(
domain.getAllContacts().stream()
.map(DesignatedContact::getContactKey)
.collect(toImmutableSet())));
XjcRdeDomain bean = DomainToXjcConverter.convertDomain(domain, RdeMode.FULL);
assertThat(bean.getRegistrant()).isNull();
assertThat(bean.getContacts()).isEmpty();

View File

@@ -27,11 +27,9 @@ import google.registry.model.common.FeatureFlagTest;
import google.registry.model.console.ConsoleUpdateHistoryTest;
import google.registry.model.console.PasswordResetRequestTest;
import google.registry.model.console.UserTest;
import google.registry.model.contact.ContactTest;
import google.registry.model.domain.DomainSqlTest;
import google.registry.model.domain.token.AllocationTokenTest;
import google.registry.model.domain.token.BulkPricingPackageTest;
import google.registry.model.history.ContactHistoryTest;
import google.registry.model.history.DomainHistoryTest;
import google.registry.model.history.HostHistoryTest;
import google.registry.model.poll.PollMessageTest;
@@ -96,8 +94,6 @@ import org.junit.runner.RunWith;
BulkPricingPackageTest.class,
ClaimsListDaoTest.class,
ConsoleUpdateHistoryTest.class,
ContactHistoryTest.class,
ContactTest.class,
CursorTest.class,
DnsRefreshRequestTest.class,
DomainSqlTest.class,

View File

@@ -73,8 +73,6 @@ import google.registry.model.common.DnsRefreshRequest;
import google.registry.model.console.GlobalRole;
import google.registry.model.console.User;
import google.registry.model.console.UserRoles;
import google.registry.model.contact.Contact;
import google.registry.model.contact.ContactAuthInfo;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainAuthInfo;
import google.registry.model.domain.DomainBase;
@@ -182,17 +180,6 @@ public final class DatabaseHelper {
.build();
}
public static Contact newContactWithRoid(String contactId, String repoId) {
return new Contact.Builder()
.setRepoId(repoId)
.setContactId(contactId)
.setCreationRegistrarId("TheRegistrar")
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("2fooBAR")))
.setCreationTimeForTest(START_OF_TIME)
.build();
}
public static Tld newTld(String tld, String roidSuffix) {
return newTld(tld, roidSuffix, ImmutableSortedMap.of(START_OF_TIME, GENERAL_AVAILABILITY));
}
@@ -232,10 +219,6 @@ public final class DatabaseHelper {
.build();
}
public static Contact persistActiveContact(String contactId) {
return persistResource(newContactWithRoid(contactId, generateNewHostRoid()));
}
public static Host persistActiveHost(String hostName) {
return persistResource(newHost(hostName));
}
@@ -1019,11 +1002,7 @@ public final class DatabaseHelper {
}
private static HistoryEntry.Type getHistoryEntryType(EppResource resource) {
if (resource instanceof Contact) {
return resource.getRepoId() != null
? HistoryEntry.Type.CONTACT_CREATE
: HistoryEntry.Type.CONTACT_UPDATE;
} else if (resource instanceof Host) {
if (resource instanceof Host) {
return resource.getRepoId() != null
? HistoryEntry.Type.HOST_CREATE
: HistoryEntry.Type.HOST_UPDATE;

View File

@@ -261,7 +261,7 @@ td.section {
</tr>
<tr>
<td class="property_name">generated on</td>
<td class="property_value">2026-01-01 02:43:24</td>
<td class="property_value">2026-02-20 05:51:31</td>
</tr>
<tr>
<td class="property_name">last flyway file</td>
@@ -273,7 +273,7 @@ td.section {
<p>&nbsp;</p>
<svg viewBox="0.00 0.00 4846.00 3765.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="erDiagram" style="overflow: hidden; width: 100%; height: 800px">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 3761)">
<title>SchemaCrawler_Diagram</title> <polygon fill="white" stroke="transparent" points="-4,4 -4,-3761 4842,-3761 4842,4 -4,4" /> <text text-anchor="start" x="4598" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated by</text> <text text-anchor="start" x="4681" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">SchemaCrawler 16.27.1</text> <text text-anchor="start" x="4597" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated on</text> <text text-anchor="start" x="4681" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">2026-01-01 02:43:24</text> <polygon fill="none" stroke="#888888" points="4594,-4 4594,-44 4830,-44 4830,-4 4594,-4" /> <!-- allocationtoken_a08ccbef -->
<title>SchemaCrawler_Diagram</title> <polygon fill="white" stroke="transparent" points="-4,4 -4,-3761 4842,-3761 4842,4 -4,4" /> <text text-anchor="start" x="4598" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated by</text> <text text-anchor="start" x="4681" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">SchemaCrawler 16.27.1</text> <text text-anchor="start" x="4597" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated on</text> <text text-anchor="start" x="4681" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">2026-02-20 05:51:31</text> <polygon fill="none" stroke="#888888" points="4594,-4 4594,-44 4830,-44 4830,-4 4594,-4" /> <!-- allocationtoken_a08ccbef -->
<g id="node1" class="node">
<title>allocationtoken_a08ccbef</title> <polygon fill="#e9c2f2" stroke="transparent" points="525.5,-1272 525.5,-1291 711.5,-1291 711.5,-1272 525.5,-1272" /> <text text-anchor="start" x="527.5" y="-1278.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."AllocationToken"</text> <polygon fill="#e9c2f2" stroke="transparent" points="711.5,-1272 711.5,-1291 785.5,-1291 785.5,-1272 711.5,-1272" /> <text text-anchor="start" x="746.5" y="-1277.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text> <text text-anchor="start" x="527.5" y="-1259.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">token</text> <text text-anchor="start" x="705.5" y="-1258.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text> <text text-anchor="start" x="713.5" y="-1258.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text> <text text-anchor="start" x="527.5" y="-1239.8" font-family="Helvetica,sans-Serif" font-size="14.00">domain_name</text> <text text-anchor="start" x="705.5" y="-1239.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text> <text text-anchor="start" x="713.5" y="-1239.8" font-family="Helvetica,sans-Serif" font-size="14.00">text</text> <text text-anchor="start" x="527.5" y="-1220.8" font-family="Helvetica,sans-Serif" font-size="14.00">redemption_domain_repo_id</text> <text text-anchor="start" x="705.5" y="-1220.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text> <text text-anchor="start" x="713.5" y="-1220.8" font-family="Helvetica,sans-Serif" font-size="14.00">text</text> <text text-anchor="start" x="527.5" y="-1201.8" font-family="Helvetica,sans-Serif" font-size="14.00">token_type</text> <text text-anchor="start" x="705.5" y="-1201.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text> <text text-anchor="start" x="713.5" y="-1201.8" font-family="Helvetica,sans-Serif" font-size="14.00">text</text> <polygon fill="none" stroke="#888888" points="524.5,-1195.5 524.5,-1292.5 786.5,-1292.5 786.5,-1195.5 524.5,-1195.5" />
</g>

File diff suppressed because one or more lines are too long

View File

@@ -145,140 +145,6 @@
primary key (revision_id)
);
create table "Contact" (
repo_id text not null,
update_timestamp timestamp(6) with time zone,
creation_registrar_id text,
creation_time timestamp(6) with time zone,
current_sponsor_registrar_id text,
deletion_time timestamp(6) with time zone,
last_epp_update_registrar_id text,
last_epp_update_time timestamp(6) with time zone,
statuses text[],
auth_info_repo_id text,
auth_info_value text,
contact_id text,
disclose_types_addr text[],
disclose_show_email boolean,
disclose_show_fax boolean,
disclose_mode_flag boolean,
disclose_types_name text[],
disclose_types_org text[],
disclose_show_voice boolean,
email text,
fax_phone_extension text,
fax_phone_number text,
addr_i18n_city text,
addr_i18n_country_code text,
addr_i18n_state text,
addr_i18n_street_line1 text,
addr_i18n_street_line2 text,
addr_i18n_street_line3 text,
addr_i18n_zip text,
addr_i18n_name text,
addr_i18n_org text,
addr_i18n_type text check (addr_i18n_type in ('LOCALIZED','INTERNATIONALIZED')),
last_transfer_time timestamp(6) with time zone,
addr_local_city text,
addr_local_country_code text,
addr_local_state text,
addr_local_street_line1 text,
addr_local_street_line2 text,
addr_local_street_line3 text,
addr_local_zip text,
addr_local_name text,
addr_local_org text,
addr_local_type text check (addr_local_type in ('LOCALIZED','INTERNATIONALIZED')),
search_name text,
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_gaining_registrar_id text,
transfer_losing_registrar_id text,
transfer_pending_expiration_time timestamp(6) with time zone,
transfer_request_time timestamp(6) with time zone,
transfer_status text check (transfer_status in ('CLIENT_APPROVED','CLIENT_CANCELLED','CLIENT_REJECTED','PENDING','SERVER_APPROVED','SERVER_CANCELLED')),
voice_phone_extension text,
voice_phone_number text,
primary key (repo_id)
);
create table "ContactHistory" (
contact_repo_id text not null,
history_revision_id bigint not null,
history_by_superuser boolean not null,
history_registrar_id text,
history_modification_time timestamp(6) with time zone not null,
history_reason text,
history_requested_by_registrar boolean,
history_client_transaction_id text,
history_server_transaction_id text,
history_type text not null check (history_type in ('CONTACT_CREATE','CONTACT_DELETE','CONTACT_DELETE_FAILURE','CONTACT_PENDING_DELETE','CONTACT_TRANSFER_APPROVE','CONTACT_TRANSFER_CANCEL','CONTACT_TRANSFER_REJECT','CONTACT_TRANSFER_REQUEST','CONTACT_UPDATE','DOMAIN_ALLOCATE','DOMAIN_AUTORENEW','DOMAIN_CREATE','DOMAIN_DELETE','DOMAIN_RENEW','DOMAIN_RESTORE','DOMAIN_TRANSFER_APPROVE','DOMAIN_TRANSFER_CANCEL','DOMAIN_TRANSFER_REJECT','DOMAIN_TRANSFER_REQUEST','DOMAIN_UPDATE','HOST_CREATE','HOST_DELETE','HOST_DELETE_FAILURE','HOST_PENDING_DELETE','HOST_UPDATE','RDE_IMPORT','SYNTHETIC')),
history_xml_bytes bytea,
auth_info_repo_id text,
auth_info_value text,
contact_id text,
disclose_types_addr text[],
disclose_show_email boolean,
disclose_show_fax boolean,
disclose_mode_flag boolean,
disclose_types_name text[],
disclose_types_org text[],
disclose_show_voice boolean,
email text,
fax_phone_extension text,
fax_phone_number text,
addr_i18n_city text,
addr_i18n_country_code text,
addr_i18n_state text,
addr_i18n_street_line1 text,
addr_i18n_street_line2 text,
addr_i18n_street_line3 text,
addr_i18n_zip text,
addr_i18n_name text,
addr_i18n_org text,
addr_i18n_type text check (addr_i18n_type in ('LOCALIZED','INTERNATIONALIZED')),
last_transfer_time timestamp(6) with time zone,
addr_local_city text,
addr_local_country_code text,
addr_local_state text,
addr_local_street_line1 text,
addr_local_street_line2 text,
addr_local_street_line3 text,
addr_local_zip text,
addr_local_name text,
addr_local_org text,
addr_local_type text check (addr_local_type in ('LOCALIZED','INTERNATIONALIZED')),
search_name text,
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_gaining_registrar_id text,
transfer_losing_registrar_id text,
transfer_pending_expiration_time timestamp(6) with time zone,
transfer_request_time timestamp(6) with time zone,
transfer_status text check (transfer_status in ('CLIENT_APPROVED','CLIENT_CANCELLED','CLIENT_REJECTED','PENDING','SERVER_APPROVED','SERVER_CANCELLED')),
voice_phone_extension text,
voice_phone_number text,
creation_registrar_id text,
creation_time timestamp(6) with time zone,
current_sponsor_registrar_id text,
deletion_time timestamp(6) with time zone,
last_epp_update_registrar_id text,
last_epp_update_time timestamp(6) with time zone,
statuses text[],
update_timestamp timestamp(6) with time zone,
primary key (contact_repo_id, history_revision_id)
);
create table "Cursor" (
scope text not null,
type text not null check (type in ('BRDA','RDE_REPORT','RDE_STAGING','RDE_UPLOAD','RDE_UPLOAD_SFTP','RECURRING_BILLING','SYNC_REGISTRAR_SHEET','ICANN_UPLOAD_TX','ICANN_UPLOAD_ACTIVITY')),
@@ -915,33 +781,6 @@
create index idx_console_update_history_modification_time
on "ConsoleUpdateHistory" (modification_time);
create index IDX3y752kr9uh4kh6uig54vemx0l
on "Contact" (creation_time);
create index IDXtm415d6fe1rr35stm33s5mg18
on "Contact" (current_sponsor_registrar_id);
create index IDXn1f711wicdnooa2mqb7g1m55o
on "Contact" (deletion_time);
create index IDXoqd7n4hbx86hvlgkilq75olas
on "Contact" (contact_id);
create index IDX1p3esngcwwu6hstyua6itn6ff
on "Contact" (search_name);
create index IDXo1xdtpij2yryh0skxe9v91sep
on "ContactHistory" (creation_time);
create index IDXhp33wybmb6tbpr1bq7ttwk8je
on "ContactHistory" (history_registrar_id);
create index IDX9q53px6r302ftgisqifmc6put
on "ContactHistory" (history_type);
create index IDXsudwswtwqnfnx2o1hx4s0k0g5
on "ContactHistory" (history_modification_time);
create index IDXhlqqd5uy98cjyos72d81x9j95
on "DelegationSignerData" (domain_repo_id);