Add JPA annotations to ContactResource and generate schema (#547)

* Add JPA annotations to ContactResource and generate schema

* Resolve comments

* Resolve comments

* Manually add foreign key constraints

* Run with junit5

* Rebase on HEAD

* Fix DomainBaseSqlTest
This commit is contained in:
Shicong Huang 2020-04-21 15:40:16 -04:00 committed by GitHub
parent b9b55c8d6e
commit 4e4c0adf5e
19 changed files with 641 additions and 133 deletions

View file

@ -70,9 +70,11 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
/** The ID of the registrar that is currently sponsoring this resource. */ /** The ID of the registrar that is currently sponsoring this resource. */
@Index @Index
@Column(nullable = false)
String currentSponsorClientId; String currentSponsorClientId;
/** The ID of the registrar that created this resource. */ /** The ID of the registrar that created this resource. */
@Column(nullable = false)
String creationClientId; String creationClientId;
/** /**
@ -88,7 +90,9 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
// Map the method to XML, not the field, because if we map the field (with an adaptor class) it // Map the method to XML, not the field, because if we map the field (with an adaptor class) it
// will never be omitted from the xml even if the timestamp inside creationTime is null and we // will never be omitted from the xml even if the timestamp inside creationTime is null and we
// return null from the adaptor. (Instead it gets written as an empty tag.) // return null from the adaptor. (Instead it gets written as an empty tag.)
@Index CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null); @Column(nullable = false)
@Index
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/** /**
* The time when this resource was or will be deleted. * The time when this resource was or will be deleted.

View file

@ -16,13 +16,14 @@ package google.registry.model.contact;
import com.googlecode.objectify.annotation.Embed; import com.googlecode.objectify.annotation.Embed;
import google.registry.model.eppcommon.Address; import google.registry.model.eppcommon.Address;
import javax.persistence.Embeddable;
/** /**
* EPP Contact Address * EPP Contact Address
* *
* <p>This class is embedded inside the {@link PostalInfo} of an EPP contact to hold its * <p>This class is embedded inside the {@link PostalInfo} of an EPP contact to hold its address.
* address. The fields are all defined in parent class {@link Address}, but the subclass is still * The fields are all defined in parent class {@link Address}, but the subclass is still necessary
* necessary to pick up the contact namespace. * to pick up the contact namespace.
* *
* <p>This does not implement {@code Overlayable} because it is intended to be bulk replaced on * <p>This does not implement {@code Overlayable} because it is intended to be bulk replaced on
* update. * update.
@ -30,6 +31,7 @@ import google.registry.model.eppcommon.Address;
* @see PostalInfo * @see PostalInfo
*/ */
@Embed @Embed
@Embeddable
public class ContactAddress extends Address { public class ContactAddress extends Address {
/** Builder for {@link ContactAddress}. */ /** Builder for {@link ContactAddress}. */

View file

@ -20,6 +20,7 @@ import javax.xml.bind.annotation.XmlType;
/** A version of authInfo specifically for contacts. */ /** A version of authInfo specifically for contacts. */
@Embed @Embed
@javax.persistence.Embeddable
@XmlType(namespace = "urn:ietf:params:xml:ns:contact-1.0") @XmlType(namespace = "urn:ietf:params:xml:ns:contact-1.0")
public class ContactAuthInfo extends AuthInfo { public class ContactAuthInfo extends AuthInfo {
public static ContactAuthInfo create(PasswordAuth pw) { public static ContactAuthInfo create(PasswordAuth pw) {

View file

@ -16,17 +16,19 @@ package google.registry.model.contact;
import com.googlecode.objectify.annotation.Embed; import com.googlecode.objectify.annotation.Embed;
import google.registry.model.eppcommon.PhoneNumber; import google.registry.model.eppcommon.PhoneNumber;
import javax.persistence.Embeddable;
/** /**
* EPP Contact Phone Number * EPP Contact Phone Number
* *
* <p>This class is embedded inside a {@link ContactResource} hold the phone number of an EPP * <p>This class is embedded inside a {@link ContactResource} hold the phone number of an EPP
* contact. The fields are all defined in the parent class {@link PhoneNumber}, but the subclass is * contact. The fields are all defined in the parent class {@link PhoneNumber}, but the subclass is
* still necessary to pick up the contact namespace. * still necessary to pick up the contact namespace.
* *
* @see ContactResource * @see ContactResource
*/ */
@Embed @Embed
@Embeddable
public class ContactPhoneNumber extends PhoneNumber { public class ContactPhoneNumber extends PhoneNumber {
/** Builder for {@link ContactPhoneNumber}. */ /** Builder for {@link ContactPhoneNumber}. */

View file

@ -33,6 +33,11 @@ import google.registry.model.transfer.TransferData;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Transient;
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElement;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -43,9 +48,19 @@ import org.joda.time.DateTime;
*/ */
@ReportedOn @ReportedOn
@Entity @Entity
@javax.persistence.Entity(name = "Contact")
@javax.persistence.Table(
name = "Contact",
indexes = {
@javax.persistence.Index(columnList = "creationTime"),
@javax.persistence.Index(columnList = "currentSponsorClientId"),
@javax.persistence.Index(columnList = "deletionTime"),
@javax.persistence.Index(columnList = "contactId", unique = true),
@javax.persistence.Index(columnList = "searchName")
})
@ExternalMessagingName("contact") @ExternalMessagingName("contact")
public class ContactResource extends EppResource implements public class ContactResource extends EppResource
ForeignKeyedEppResource, ResourceWithTransferData { implements ForeignKeyedEppResource, ResourceWithTransferData {
/** /**
* Unique identifier for this contact. * Unique identifier for this contact.
@ -61,13 +76,55 @@ public class ContactResource extends EppResource implements
* US-ASCII character set. Personal info; cleared by {@link Builder#wipeOut}. * US-ASCII character set. Personal info; cleared by {@link Builder#wipeOut}.
*/ */
@IgnoreSave(IfNull.class) @IgnoreSave(IfNull.class)
@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; PostalInfo localizedPostalInfo;
/** /**
* Internationalized postal info for the contact. Personal info; cleared by * Internationalized postal info for the contact. Personal info; cleared by {@link
* {@link Builder#wipeOut}. * Builder#wipeOut}.
*/ */
@IgnoreSave(IfNull.class) @IgnoreSave(IfNull.class)
@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; PostalInfo internationalizedPostalInfo;
/** /**
@ -80,10 +137,20 @@ public class ContactResource extends EppResource implements
/** Contacts voice number. Personal info; cleared by {@link Builder#wipeOut}. */ /** Contacts voice number. Personal info; cleared by {@link Builder#wipeOut}. */
@IgnoreSave(IfNull.class) @IgnoreSave(IfNull.class)
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "phoneNumber", column = @Column(name = "voice_phone_number")),
@AttributeOverride(name = "extension", column = @Column(name = "voice_phone_extension")),
})
ContactPhoneNumber voice; ContactPhoneNumber voice;
/** Contacts fax number. Personal info; cleared by {@link Builder#wipeOut}. */ /** Contacts fax number. Personal info; cleared by {@link Builder#wipeOut}. */
@IgnoreSave(IfNull.class) @IgnoreSave(IfNull.class)
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "phoneNumber", column = @Column(name = "fax_phone_number")),
@AttributeOverride(name = "extension", column = @Column(name = "fax_phone_extension")),
})
ContactPhoneNumber fax; ContactPhoneNumber fax;
/** Contacts email address. Personal info; cleared by {@link Builder#wipeOut}. */ /** Contacts email address. Personal info; cleared by {@link Builder#wipeOut}. */
@ -91,10 +158,16 @@ public class ContactResource extends EppResource implements
String email; String email;
/** Authorization info (aka transfer secret) of the contact. */ /** 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; ContactAuthInfo authInfo;
/** Data about any pending or past transfers on this contact. */ /** Data about any pending or past transfers on this contact. */
TransferData transferData; // TODO(b/153363295): Figure out how to persist transfer data
@Transient TransferData transferData;
/** /**
* The time that this resource was last transferred. * The time that this resource was last transferred.
@ -107,6 +180,16 @@ public class ContactResource extends EppResource implements
// the wipeOut() function, so that data is not kept around for deleted contacts. // the wipeOut() function, so that data is not kept around for deleted contacts.
/** Disclosure policy. */ /** 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; Disclose disclose;
public String getContactId() { public String getContactId() {

View file

@ -22,11 +22,14 @@ import google.registry.model.Buildable;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import google.registry.model.eppcommon.PresenceMarker; import google.registry.model.eppcommon.PresenceMarker;
import java.util.List; import java.util.List;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlType;
/** The "discloseType" from {@link "http://tools.ietf.org/html/rfc5733"}. */ /** The "discloseType" from {@link "http://tools.ietf.org/html/rfc5733"}. */
@Embed @Embed
@Embeddable
@XmlType(propOrder = {"name", "org", "addr", "voice", "fax", "email"}) @XmlType(propOrder = {"name", "org", "addr", "voice", "fax", "email"})
public class Disclose extends ImmutableObject { public class Disclose extends ImmutableObject {
@ -36,11 +39,11 @@ public class Disclose extends ImmutableObject {
List<PostalInfoChoice> addr; List<PostalInfoChoice> addr;
PresenceMarker voice; @Embedded PresenceMarker voice;
PresenceMarker fax; @Embedded PresenceMarker fax;
PresenceMarker email; @Embedded PresenceMarker email;
@XmlAttribute @XmlAttribute
Boolean flag; Boolean flag;

View file

@ -21,6 +21,9 @@ import google.registry.model.Buildable;
import google.registry.model.Buildable.Overlayable; import google.registry.model.Buildable.Overlayable;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import java.util.Optional; import java.util.Optional;
import javax.persistence.Embeddable;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlEnumValue; import javax.xml.bind.annotation.XmlEnumValue;
@ -29,10 +32,11 @@ import javax.xml.bind.annotation.adapters.NormalizedStringAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
/** /**
* Implementation of both "postalInfoType" and "chgPostalInfoType" from * Implementation of both "postalInfoType" and "chgPostalInfoType" from {@link
* {@link "http://tools.ietf.org/html/rfc5733"}. * "http://tools.ietf.org/html/rfc5733"}.
*/ */
@Embed @Embed
@Embeddable
@XmlType(propOrder = {"name", "org", "address", "type"}) @XmlType(propOrder = {"name", "org", "address", "type"})
public class PostalInfo extends ImmutableObject implements Overlayable<PostalInfo> { public class PostalInfo extends ImmutableObject implements Overlayable<PostalInfo> {
@ -53,6 +57,7 @@ public class PostalInfo extends ImmutableObject implements Overlayable<PostalInf
@XmlElement(name = "addr") @XmlElement(name = "addr")
ContactAddress address; ContactAddress address;
@Enumerated(EnumType.STRING)
@XmlAttribute @XmlAttribute
Type type; Type type;

View file

@ -18,6 +18,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
import google.registry.model.Buildable; import google.registry.model.Buildable;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import javax.persistence.Embeddable;
import javax.persistence.MappedSuperclass;
import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlValue; import javax.xml.bind.annotation.XmlValue;
@ -31,17 +33,21 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
* "e164Type" type from {@link "http://tools.ietf.org/html/draft-lozano-tmch-smd"}. * "e164Type" type from {@link "http://tools.ietf.org/html/draft-lozano-tmch-smd"}.
* *
* <blockquote> * <blockquote>
*
* <p>"Contact telephone number structure is derived from structures defined in [ITU.E164.2005]. * <p>"Contact telephone number structure is derived from structures defined in [ITU.E164.2005].
* Telephone numbers described in this mapping are character strings that MUST begin with a plus * Telephone numbers described in this mapping are character strings that MUST begin with a plus
* sign ("+", ASCII value 0x002B), followed by a country code defined in [ITU.E164.2005], followed * sign ("+", ASCII value 0x002B), followed by a country code defined in [ITU.E164.2005], followed
* by a dot (".", ASCII value 0x002E), followed by a sequence of digits representing the telephone * by a dot (".", ASCII value 0x002E), followed by a sequence of digits representing the telephone
* number. An optional "x" attribute is provided to note telephone extension information." * number. An optional "x" attribute is provided to note telephone extension information."
*
* </blockquote> * </blockquote>
* *
* @see google.registry.model.contact.ContactPhoneNumber * @see google.registry.model.contact.ContactPhoneNumber
* @see google.registry.model.mark.MarkPhoneNumber * @see google.registry.model.mark.MarkPhoneNumber
*/ */
@XmlTransient @XmlTransient
@Embeddable
@MappedSuperclass
public class PhoneNumber extends ImmutableObject { public class PhoneNumber extends ImmutableObject {
@XmlValue @XmlValue

View file

@ -17,6 +17,7 @@ package google.registry.model.eppcommon;
import com.googlecode.objectify.annotation.Embed; import com.googlecode.objectify.annotation.Embed;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import java.io.Serializable; import java.io.Serializable;
import javax.persistence.Embeddable;
import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlTransient;
/** /**
@ -26,6 +27,7 @@ import javax.xml.bind.annotation.XmlTransient;
* {@code <foo></foo>}, and will unmarshal always to {@code <foo/>}. * {@code <foo></foo>}, and will unmarshal always to {@code <foo/>}.
*/ */
@Embed @Embed
@Embeddable
public class PresenceMarker extends ImmutableObject implements Serializable { public class PresenceMarker extends ImmutableObject implements Serializable {
@XmlTransient @XmlTransient
boolean marked = true; boolean marked = true;

View file

@ -18,6 +18,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -31,6 +32,7 @@ import javax.persistence.PostUpdate;
import javax.persistence.PrePersist; import javax.persistence.PrePersist;
import javax.persistence.PreRemove; import javax.persistence.PreRemove;
import javax.persistence.PreUpdate; import javax.persistence.PreUpdate;
import javax.persistence.Transient;
/** /**
* A listener class to invoke entity callbacks in cases where Hibernate doesn't invoke the callback * A listener class to invoke entity callbacks in cases where Hibernate doesn't invoke the callback
@ -167,10 +169,12 @@ public class EntityCallbacksListener {
private Stream<Object> findEmbeddedProperties(Object object, Class<?> clazz) { private Stream<Object> findEmbeddedProperties(Object object, Class<?> clazz) {
return Arrays.stream(clazz.getDeclaredFields()) return Arrays.stream(clazz.getDeclaredFields())
.filter(field -> !field.isAnnotationPresent(Transient.class))
.filter( .filter(
field -> field ->
field.isAnnotationPresent(Embedded.class) field.isAnnotationPresent(Embedded.class)
|| field.getType().isAnnotationPresent(Embeddable.class)) || field.getType().isAnnotationPresent(Embeddable.class))
.filter(field -> !Modifier.isStatic(field.getModifiers()))
.map(field -> getFieldObject(field, object)) .map(field -> getFieldObject(field, object))
.filter(Objects::nonNull); .filter(Objects::nonNull);
} }

View file

@ -0,0 +1,36 @@
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.persistence.converter;
import google.registry.model.contact.Disclose.PostalInfoChoice;
import google.registry.model.contact.PostalInfo;
import java.util.List;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
/** JPA {@link AttributeConverter} for storing/retrieving {@link List < PostalInfoChoice >}. */
@Converter(autoApply = true)
public class PostalInfoChoiceListConverter extends StringListConverterBase<PostalInfoChoice> {
@Override
String toString(PostalInfoChoice element) {
return element.getType().name();
}
@Override
PostalInfoChoice fromString(String value) {
return PostalInfoChoice.create(PostalInfo.Type.valueOf(value));
}
}

View file

@ -19,6 +19,7 @@
* Move tests to another (sub)project. This is not a big problem, but feels unnatural. * Move tests to another (sub)project. This is not a big problem, but feels unnatural.
* Use Hibernate's ServiceRegistry for bootstrapping (not JPA-compliant) * Use Hibernate's ServiceRegistry for bootstrapping (not JPA-compliant)
--> -->
<class>google.registry.model.contact.ContactResource</class>
<class>google.registry.model.domain.DomainBase</class> <class>google.registry.model.domain.DomainBase</class>
<class>google.registry.model.host.HostResource</class> <class>google.registry.model.host.HostResource</class>
<class>google.registry.model.registrar.Registrar</class> <class>google.registry.model.registrar.Registrar</class>
@ -41,6 +42,7 @@
<class>google.registry.persistence.converter.CurrencyUnitConverter</class> <class>google.registry.persistence.converter.CurrencyUnitConverter</class>
<class>google.registry.persistence.converter.DateTimeConverter</class> <class>google.registry.persistence.converter.DateTimeConverter</class>
<class>google.registry.persistence.converter.DurationConverter</class> <class>google.registry.persistence.converter.DurationConverter</class>
<class>google.registry.persistence.converter.PostalInfoChoiceListConverter</class>
<class>google.registry.persistence.converter.RegistrarPocSetConverter</class> <class>google.registry.persistence.converter.RegistrarPocSetConverter</class>
<class>google.registry.persistence.converter.StatusValueSetConverter</class> <class>google.registry.persistence.converter.StatusValueSetConverter</class>
<class>google.registry.persistence.converter.StringListConverter</class> <class>google.registry.persistence.converter.StringListConverter</class>

View file

@ -17,10 +17,13 @@ package google.registry.model.contact;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat; import static com.google.common.truth.Truth8.assertThat;
import static google.registry.model.EppResourceUtils.loadByForeignKey; import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.testing.ContactResourceSubject.assertAboutContacts; import static google.registry.testing.ContactResourceSubject.assertAboutContacts;
import static google.registry.testing.DatastoreHelper.cloneAndSetAutoTimestamps; import static google.registry.testing.DatastoreHelper.cloneAndSetAutoTimestamps;
import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.SqlHelper.assertThrowForeignKeyViolation;
import static google.registry.testing.SqlHelper.saveRegistrar;
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
@ -37,87 +40,119 @@ import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferStatus; import google.registry.model.transfer.TransferStatus;
import org.junit.Before; import google.registry.persistence.VKey;
import org.junit.Test; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link ContactResource}. */ /** Unit tests for {@link ContactResource}. */
public class ContactResourceTest extends EntityTestCase { public class ContactResourceTest extends EntityTestCase {
ContactResource originalContact;
ContactResource contactResource; ContactResource contactResource;
@Before public ContactResourceTest() {
super(true);
}
@BeforeEach
public void setUp() { public void setUp() {
createTld("foobar"); createTld("foobar");
originalContact =
new ContactResource.Builder()
.setContactId("contact_id")
.setRepoId("1-FOOBAR")
.setCreationClientId("registrar1")
.setLastEppUpdateTime(fakeClock.nowUtc())
.setLastEppUpdateClientId("registrar2")
.setLastTransferTime(fakeClock.nowUtc())
.setPersistedCurrentSponsorClientId("registrar3")
.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 TransferData.Builder()
.setGainingClientId("gaining")
.setLosingClientId("losing")
.setPendingTransferExpirationTime(fakeClock.nowUtc())
.setServerApproveEntities(
ImmutableSet.of(Key.create(BillingEvent.OneTime.class, 1)))
.setTransferRequestTime(fakeClock.nowUtc())
.setTransferStatus(TransferStatus.SERVER_APPROVED)
.setTransferRequestTrid(Trid.create("client-trid", "server-trid"))
.build())
.build();
// Set up a new persisted ContactResource entity. // Set up a new persisted ContactResource entity.
contactResource = contactResource = persistResource(cloneAndSetAutoTimestamps(originalContact));
persistResource( }
cloneAndSetAutoTimestamps(
new ContactResource.Builder() @Test
.setContactId("contact_id") public void testCloudSqlPersistence_failWhenViolateForeignKeyConstraint() {
.setRepoId("1-FOOBAR") assertThrowForeignKeyViolation(() -> jpaTm().transact(() -> jpaTm().saveNew(originalContact)));
.setCreationClientId("a registrar") }
.setLastEppUpdateTime(fakeClock.nowUtc())
.setLastEppUpdateClientId("another registrar") @Test
.setLastTransferTime(fakeClock.nowUtc()) public void testCloudSqlPersistence_succeed() {
.setPersistedCurrentSponsorClientId("a third registrar") saveRegistrar("registrar1");
.setLocalizedPostalInfo( saveRegistrar("registrar2");
new PostalInfo.Builder() saveRegistrar("registrar3");
.setType(Type.LOCALIZED) jpaTm().transact(() -> jpaTm().saveNew(originalContact));
.setAddress( ContactResource persisted =
new ContactAddress.Builder() jpaTm()
.setStreet(ImmutableList.of("111 8th Ave", "4th Floor")) .transact(
.setCity("New York") () ->
.setState("NY") jpaTm()
.setZip("10011") .load(VKey.createSql(ContactResource.class, originalContact.getRepoId())))
.setCountryCode("US") .get();
.build()) // TODO(b/153378849): Remove the hard code for postal info after resolving the issue that
.build()) // @PostLoad doesn't work in Address
.setInternationalizedPostalInfo( ContactResource fixed =
new PostalInfo.Builder() originalContact
.setType(Type.INTERNATIONALIZED) .asBuilder()
.setAddress( .setCreationTime(persisted.getCreationTime())
new ContactAddress.Builder() .setInternationalizedPostalInfo(persisted.getInternationalizedPostalInfo())
.setStreet(ImmutableList.of("111 8th Ave", "4th Floor")) .setLocalizedPostalInfo(persisted.getLocalizedPostalInfo())
.setCity("New York") .setTransferData(null)
.setState("NY") .build();
.setZip("10011") assertThat(persisted).isEqualTo(fixed);
.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 TransferData.Builder()
.setGainingClientId("gaining")
.setLosingClientId("losing")
.setPendingTransferExpirationTime(fakeClock.nowUtc())
.setServerApproveEntities(
ImmutableSet.of(Key.create(BillingEvent.OneTime.class, 1)))
.setTransferRequestTime(fakeClock.nowUtc())
.setTransferStatus(TransferStatus.SERVER_APPROVED)
.setTransferRequestTrid(Trid.create("client-trid", "server-trid"))
.build())
.build()));
} }
@Test @Test

View file

@ -16,9 +16,10 @@ package google.registry.model.domain;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm; import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.testing.SqlHelper.assertThrowForeignKeyViolation;
import static google.registry.testing.SqlHelper.saveRegistrar;
import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.time.DateTimeZone.UTC; import static org.joda.time.DateTimeZone.UTC;
import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
@ -34,9 +35,7 @@ import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageExtension; import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageExtension;
import google.registry.testing.DatastoreEntityExtension; import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.FakeClock; import google.registry.testing.FakeClock;
import java.sql.SQLException;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.RollbackException;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Order;
@ -73,9 +72,9 @@ public class DomainBaseSqlTest {
new DomainBase.Builder() new DomainBase.Builder()
.setFullyQualifiedDomainName("example.com") .setFullyQualifiedDomainName("example.com")
.setRepoId("4-COM") .setRepoId("4-COM")
.setCreationClientId("a registrar") .setCreationClientId("registrar1")
.setLastEppUpdateTime(fakeClock.nowUtc()) .setLastEppUpdateTime(fakeClock.nowUtc())
.setLastEppUpdateClientId("AnotherRegistrar") .setLastEppUpdateClientId("registrar2")
.setLastTransferTime(fakeClock.nowUtc()) .setLastTransferTime(fakeClock.nowUtc())
.setNameservers(host1VKey) .setNameservers(host1VKey)
.setStatusValues( .setStatusValues(
@ -89,7 +88,7 @@ public class DomainBaseSqlTest {
.setRegistrant(contactKey) .setRegistrant(contactKey)
.setContacts(ImmutableSet.of(DesignatedContact.create(Type.ADMIN, contact2Key))) .setContacts(ImmutableSet.of(DesignatedContact.create(Type.ADMIN, contact2Key)))
.setSubordinateHosts(ImmutableSet.of("ns1.example.com")) .setSubordinateHosts(ImmutableSet.of("ns1.example.com"))
.setPersistedCurrentSponsorClientId("losing") .setPersistedCurrentSponsorClientId("registrar3")
.setRegistrationExpirationTime(fakeClock.nowUtc().plusYears(1)) .setRegistrationExpirationTime(fakeClock.nowUtc().plusYears(1))
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("password"))) .setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("password")))
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2}))) .setDsData(ImmutableSet.of(DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2})))
@ -102,11 +101,17 @@ public class DomainBaseSqlTest {
new HostResource.Builder() new HostResource.Builder()
.setRepoId("host1") .setRepoId("host1")
.setFullyQualifiedHostName("ns1.example.com") .setFullyQualifiedHostName("ns1.example.com")
.setCreationClientId("registrar1")
.setPersistedCurrentSponsorClientId("registrar2")
.build(); .build();
} }
@Test @Test
public void testDomainBasePersistence() { public void testDomainBasePersistence() {
saveRegistrar("registrar1");
saveRegistrar("registrar2");
saveRegistrar("registrar3");
jpaTm() jpaTm()
.transact( .transact(
() -> { () -> {
@ -147,28 +152,15 @@ public class DomainBaseSqlTest {
@Test @Test
public void testForeignKeyConstraints() { public void testForeignKeyConstraints() {
Exception e = assertThrowForeignKeyViolation(
assertThrows( () -> {
RollbackException.class, jpaTm()
() -> { .transact(
jpaTm() () -> {
.transact( // Persist the domain without the associated host object.
() -> { EntityManager em = jpaTm().getEntityManager();
// Persist the domain without the associated host object. em.persist(domain);
EntityManager em = jpaTm().getEntityManager(); });
em.persist(domain); });
});
});
assertThat(e)
.hasCauseThat() // ConstraintViolationException
.hasCauseThat() // ConstraintViolationException
.hasCauseThat()
.isInstanceOf(SQLException.class);
assertThat(e)
.hasCauseThat() // ConstraintViolationException
.hasCauseThat() // ConstraintViolationException
.hasCauseThat()
.hasMessageThat()
.contains("\"DomainHost\" violates foreign key constraint \"fk_domainhost_host");
} }
} }

View file

@ -16,6 +16,7 @@ package google.registry.schema.integration;
import static com.google.common.truth.Truth.assert_; import static com.google.common.truth.Truth.assert_;
import google.registry.model.contact.ContactResourceTest;
import google.registry.model.domain.DomainBaseSqlTest; import google.registry.model.domain.DomainBaseSqlTest;
import google.registry.model.registry.RegistryLockDaoTest; import google.registry.model.registry.RegistryLockDaoTest;
import google.registry.persistence.transaction.JpaEntityCoverage; import google.registry.persistence.transaction.JpaEntityCoverage;
@ -68,6 +69,7 @@ import org.junit.runner.RunWith;
// BeforeSuiteTest must be the first entry. See class javadoc for details. // BeforeSuiteTest must be the first entry. See class javadoc for details.
BeforeSuiteTest.class, BeforeSuiteTest.class,
ClaimsListDaoTest.class, ClaimsListDaoTest.class,
ContactResourceTest.class,
CursorDaoTest.class, CursorDaoTest.class,
DomainBaseSqlTest.class, DomainBaseSqlTest.class,
LockDaoTest.class, LockDaoTest.class,

View file

@ -14,12 +14,19 @@
package google.registry.testing; package google.registry.testing;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm; import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.testing.AppEngineRule.makeRegistrar1;
import static org.junit.Assert.assertThrows;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import google.registry.model.registry.RegistryLockDao; import google.registry.model.registry.RegistryLockDao;
import google.registry.schema.domain.RegistryLock; import google.registry.schema.domain.RegistryLock;
import java.sql.SQLException;
import java.util.Optional; import java.util.Optional;
import javax.persistence.RollbackException;
import org.junit.function.ThrowingRunnable;
/** Static utils for setting up and retrieving test resources from the SQL database. */ /** Static utils for setting up and retrieving test resources from the SQL database. */
public class SqlHelper { public class SqlHelper {
@ -52,5 +59,19 @@ public class SqlHelper {
return jpaTm().transact(() -> RegistryLockDao.getByRevisionId(revisionId)); return jpaTm().transact(() -> RegistryLockDao.getByRevisionId(revisionId));
} }
public static void saveRegistrar(String clientId) {
jpaTm()
.transact(
() -> jpaTm().saveNew(makeRegistrar1().asBuilder().setClientId(clientId).build()));
}
public static void assertThrowForeignKeyViolation(ThrowingRunnable runnable) {
RollbackException thrown = assertThrows(RollbackException.class, runnable);
assertThat(Throwables.getRootCause(thrown)).isInstanceOf(SQLException.class);
assertThat(Throwables.getRootCause(thrown))
.hasMessageThat()
.contains("violates foreign key constraint");
}
private SqlHelper() {} private SqlHelper() {}
} }

View file

@ -0,0 +1,107 @@
-- Copyright 2020 The Nomulus Authors. All Rights Reserved.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
create table "Contact" (
repo_id text not null,
creation_client_id text not null,
creation_time timestamptz not null,
current_sponsor_client_id text not null,
deletion_time timestamptz,
last_epp_update_client_id text,
last_epp_update_time timestamptz,
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,
last_transfer_time timestamptz,
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,
search_name text,
voice_phone_extension text,
voice_phone_number text,
primary key (repo_id)
);
create index IDX3y752kr9uh4kh6uig54vemx0l on "Contact" (creation_time);
create index IDXbn8t4wp85fgxjl8q4ctlscx55 on "Contact" (current_sponsor_client_id);
create index IDXn1f711wicdnooa2mqb7g1m55o on "Contact" (deletion_time);
create index IDX1p3esngcwwu6hstyua6itn6ff on "Contact" (search_name);
alter table if exists "Contact"
add constraint UKoqd7n4hbx86hvlgkilq75olas unique (contact_id);
alter table "Domain" alter column creation_time set not null;
alter table "Domain" alter column creation_client_id set not null;
alter table "Domain" alter column current_sponsor_client_id set not null;
drop index IDX8ffrqm27qtj20jac056j7yq07;
create index IDXkjt9yaq92876dstimd93hwckh on "Domain" (current_sponsor_client_id);
alter table if exists "Contact"
add constraint FK1sfyj7o7954prbn1exk7lpnoe
foreign key (creation_client_id)
references "Registrar";
alter table if exists "Contact"
add constraint FK93c185fx7chn68uv7nl6uv2s0
foreign key (current_sponsor_client_id)
references "Registrar";
alter table if exists "Contact"
add constraint FKmb7tdiv85863134w1wogtxrb2
foreign key (last_epp_update_client_id)
references "Registrar";
alter table if exists "Domain"
add constraint FK2jc69qyg2tv9hhnmif6oa1cx1
foreign key (creation_client_id)
references "Registrar";
alter table if exists "Domain"
add constraint FK2u3srsfbei272093m3b3xwj23
foreign key (current_sponsor_client_id)
references "Registrar";
alter table if exists "Domain"
add constraint FKjc0r9r5y1lfbt4gpbqw4wsuvq
foreign key (last_epp_update_client_id)
references "Registrar";

View file

@ -26,6 +26,55 @@
primary key (revision_id) primary key (revision_id)
); );
create table "Contact" (
repo_id text not null,
creation_client_id text not null,
creation_time timestamptz not null,
current_sponsor_client_id text not null,
deletion_time timestamptz,
last_epp_update_client_id text,
last_epp_update_time timestamptz,
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,
last_transfer_time timestamptz,
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,
search_name text,
voice_phone_extension text,
voice_phone_number text,
primary key (repo_id)
);
create table "Cursor" ( create table "Cursor" (
scope text not null, scope text not null,
type text not null, type text not null,
@ -44,9 +93,9 @@
create table "Domain" ( create table "Domain" (
repo_id text not null, repo_id text not null,
creation_client_id text, creation_client_id text not null,
creation_time timestamptz, creation_time timestamptz not null,
current_sponsor_client_id text, current_sponsor_client_id text not null,
deletion_time timestamptz, deletion_time timestamptz,
last_epp_update_client_id text, last_epp_update_client_id text,
last_epp_update_time timestamptz, last_epp_update_time timestamptz,
@ -84,9 +133,9 @@
create table "HostResource" ( create table "HostResource" (
repo_id text not null, repo_id text not null,
creation_client_id text, creation_client_id text not null,
creation_time timestamptz, creation_time timestamptz not null,
current_sponsor_client_id text, current_sponsor_client_id text not null,
deletion_time timestamptz, deletion_time timestamptz,
last_epp_update_client_id text, last_epp_update_client_id text,
last_epp_update_time timestamptz, last_epp_update_time timestamptz,
@ -227,6 +276,13 @@
should_publish boolean not null, should_publish boolean not null,
primary key (revision_id) primary key (revision_id)
); );
create index IDX3y752kr9uh4kh6uig54vemx0l on "Contact" (creation_time);
create index IDXbn8t4wp85fgxjl8q4ctlscx55 on "Contact" (current_sponsor_client_id);
create index IDXn1f711wicdnooa2mqb7g1m55o on "Contact" (deletion_time);
create index IDX1p3esngcwwu6hstyua6itn6ff on "Contact" (search_name);
alter table if exists "Contact"
add constraint UKoqd7n4hbx86hvlgkilq75olas unique (contact_id);
create index IDX8nr0ke9mrrx4ewj6pd2ag4rmr on "Domain" (creation_time); create index IDX8nr0ke9mrrx4ewj6pd2ag4rmr on "Domain" (creation_time);
create index IDX8ffrqm27qtj20jac056j7yq07 on "Domain" (current_sponsor_client_id); create index IDX8ffrqm27qtj20jac056j7yq07 on "Domain" (current_sponsor_client_id);
create index IDX5mnf0wn20tno4b9do88j61klr on "Domain" (deletion_time); create index IDX5mnf0wn20tno4b9do88j61klr on "Domain" (deletion_time);

View file

@ -75,6 +75,59 @@ CREATE SEQUENCE public."ClaimsList_revision_id_seq"
ALTER SEQUENCE public."ClaimsList_revision_id_seq" OWNED BY public."ClaimsList".revision_id; ALTER SEQUENCE public."ClaimsList_revision_id_seq" OWNED BY public."ClaimsList".revision_id;
--
-- Name: Contact; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public."Contact" (
repo_id text NOT NULL,
creation_client_id text NOT NULL,
creation_time timestamp with time zone NOT NULL,
current_sponsor_client_id text NOT NULL,
deletion_time timestamp with time zone,
last_epp_update_client_id text,
last_epp_update_time timestamp 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,
last_transfer_time timestamp 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,
search_name text,
voice_phone_extension text,
voice_phone_number text
);
-- --
-- Name: Cursor; Type: TABLE; Schema: public; Owner: - -- Name: Cursor; Type: TABLE; Schema: public; Owner: -
-- --
@ -93,9 +146,9 @@ CREATE TABLE public."Cursor" (
CREATE TABLE public."Domain" ( CREATE TABLE public."Domain" (
repo_id text NOT NULL, repo_id text NOT NULL,
creation_client_id text, creation_client_id text NOT NULL,
creation_time timestamp with time zone, creation_time timestamp with time zone NOT NULL,
current_sponsor_client_id text, current_sponsor_client_id text NOT NULL,
deletion_time timestamp with time zone, deletion_time timestamp with time zone,
last_epp_update_client_id text, last_epp_update_client_id text,
last_epp_update_time timestamp with time zone, last_epp_update_time timestamp with time zone,
@ -414,6 +467,14 @@ ALTER TABLE ONLY public."ClaimsList"
ADD CONSTRAINT "ClaimsList_pkey" PRIMARY KEY (revision_id); ADD CONSTRAINT "ClaimsList_pkey" PRIMARY KEY (revision_id);
--
-- Name: Contact Contact_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Contact"
ADD CONSTRAINT "Contact_pkey" PRIMARY KEY (repo_id);
-- --
-- Name: Cursor Cursor_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- Name: Cursor Cursor_pkey; Type: CONSTRAINT; Schema: public; Owner: -
-- --
@ -510,6 +571,21 @@ ALTER TABLE ONLY public."RegistryLock"
ADD CONSTRAINT idx_registry_lock_repo_id_revision_id UNIQUE (repo_id, revision_id); ADD CONSTRAINT idx_registry_lock_repo_id_revision_id UNIQUE (repo_id, revision_id);
--
-- Name: Contact ukoqd7n4hbx86hvlgkilq75olas; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Contact"
ADD CONSTRAINT ukoqd7n4hbx86hvlgkilq75olas UNIQUE (contact_id);
--
-- Name: idx1p3esngcwwu6hstyua6itn6ff; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX idx1p3esngcwwu6hstyua6itn6ff ON public."Contact" USING btree (search_name);
-- --
-- Name: idx1rcgkdd777bpvj0r94sltwd5y; Type: INDEX; Schema: public; Owner: - -- Name: idx1rcgkdd777bpvj0r94sltwd5y; Type: INDEX; Schema: public; Owner: -
-- --
@ -517,6 +593,13 @@ ALTER TABLE ONLY public."RegistryLock"
CREATE INDEX idx1rcgkdd777bpvj0r94sltwd5y ON public."Domain" USING btree (fully_qualified_domain_name); CREATE INDEX idx1rcgkdd777bpvj0r94sltwd5y ON public."Domain" USING btree (fully_qualified_domain_name);
--
-- Name: idx3y752kr9uh4kh6uig54vemx0l; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX idx3y752kr9uh4kh6uig54vemx0l ON public."Contact" USING btree (creation_time);
-- --
-- Name: idx5mnf0wn20tno4b9do88j61klr; Type: INDEX; Schema: public; Owner: - -- Name: idx5mnf0wn20tno4b9do88j61klr; Type: INDEX; Schema: public; Owner: -
-- --
@ -524,13 +607,6 @@ CREATE INDEX idx1rcgkdd777bpvj0r94sltwd5y ON public."Domain" USING btree (fully_
CREATE INDEX idx5mnf0wn20tno4b9do88j61klr ON public."Domain" USING btree (deletion_time); CREATE INDEX idx5mnf0wn20tno4b9do88j61klr ON public."Domain" USING btree (deletion_time);
--
-- Name: idx8ffrqm27qtj20jac056j7yq07; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX idx8ffrqm27qtj20jac056j7yq07 ON public."Domain" USING btree (current_sponsor_client_id);
-- --
-- Name: idx8nr0ke9mrrx4ewj6pd2ag4rmr; Type: INDEX; Schema: public; Owner: - -- Name: idx8nr0ke9mrrx4ewj6pd2ag4rmr; Type: INDEX; Schema: public; Owner: -
-- --
@ -552,6 +628,27 @@ CREATE INDEX idx_registry_lock_registrar_id ON public."RegistryLock" USING btree
CREATE INDEX idx_registry_lock_verification_code ON public."RegistryLock" USING btree (verification_code); CREATE INDEX idx_registry_lock_verification_code ON public."RegistryLock" USING btree (verification_code);
--
-- Name: idxbn8t4wp85fgxjl8q4ctlscx55; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX idxbn8t4wp85fgxjl8q4ctlscx55 ON public."Contact" USING btree (current_sponsor_client_id);
--
-- Name: idxkjt9yaq92876dstimd93hwckh; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX idxkjt9yaq92876dstimd93hwckh ON public."Domain" USING btree (current_sponsor_client_id);
--
-- Name: idxn1f711wicdnooa2mqb7g1m55o; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX idxn1f711wicdnooa2mqb7g1m55o ON public."Contact" USING btree (deletion_time);
-- --
-- Name: idxrwl38wwkli1j7gkvtywi9jokq; Type: INDEX; Schema: public; Owner: - -- Name: idxrwl38wwkli1j7gkvtywi9jokq; Type: INDEX; Schema: public; Owner: -
-- --
@ -594,6 +691,22 @@ CREATE INDEX registrarpoc_gae_user_id_idx ON public."RegistrarPoc" USING btree (
CREATE INDEX reservedlist_name_idx ON public."ReservedList" USING btree (name); CREATE INDEX reservedlist_name_idx ON public."ReservedList" USING btree (name);
--
-- Name: Contact fk1sfyj7o7954prbn1exk7lpnoe; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Contact"
ADD CONSTRAINT fk1sfyj7o7954prbn1exk7lpnoe FOREIGN KEY (creation_client_id) REFERENCES public."Registrar"(client_id);
--
-- Name: Domain fk2jc69qyg2tv9hhnmif6oa1cx1; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Domain"
ADD CONSTRAINT fk2jc69qyg2tv9hhnmif6oa1cx1 FOREIGN KEY (creation_client_id) REFERENCES public."Registrar"(client_id);
-- --
-- Name: RegistryLock fk2lhcwpxlnqijr96irylrh1707; Type: FK CONSTRAINT; Schema: public; Owner: - -- Name: RegistryLock fk2lhcwpxlnqijr96irylrh1707; Type: FK CONSTRAINT; Schema: public; Owner: -
-- --
@ -602,6 +715,14 @@ ALTER TABLE ONLY public."RegistryLock"
ADD CONSTRAINT fk2lhcwpxlnqijr96irylrh1707 FOREIGN KEY (relock_revision_id) REFERENCES public."RegistryLock"(revision_id); ADD CONSTRAINT fk2lhcwpxlnqijr96irylrh1707 FOREIGN KEY (relock_revision_id) REFERENCES public."RegistryLock"(revision_id);
--
-- Name: Domain fk2u3srsfbei272093m3b3xwj23; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Domain"
ADD CONSTRAINT fk2u3srsfbei272093m3b3xwj23 FOREIGN KEY (current_sponsor_client_id) REFERENCES public."Registrar"(client_id);
-- --
-- Name: ClaimsEntry fk6sc6at5hedffc0nhdcab6ivuq; Type: FK CONSTRAINT; Schema: public; Owner: - -- Name: ClaimsEntry fk6sc6at5hedffc0nhdcab6ivuq; Type: FK CONSTRAINT; Schema: public; Owner: -
-- --
@ -618,6 +739,14 @@ ALTER TABLE ONLY public."HostResource_inetAddresses"
ADD CONSTRAINT fk6unwhfkcu3oq6q347fxvpagv FOREIGN KEY (host_resource_repo_id) REFERENCES public."HostResource"(repo_id); ADD CONSTRAINT fk6unwhfkcu3oq6q347fxvpagv FOREIGN KEY (host_resource_repo_id) REFERENCES public."HostResource"(repo_id);
--
-- Name: Contact fk93c185fx7chn68uv7nl6uv2s0; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Contact"
ADD CONSTRAINT fk93c185fx7chn68uv7nl6uv2s0 FOREIGN KEY (current_sponsor_client_id) REFERENCES public."Registrar"(client_id);
-- --
-- Name: DomainHost fk_domainhost_host_valid; Type: FK CONSTRAINT; Schema: public; Owner: - -- Name: DomainHost fk_domainhost_host_valid; Type: FK CONSTRAINT; Schema: public; Owner: -
-- --
@ -642,6 +771,22 @@ ALTER TABLE ONLY public."ReservedEntry"
ADD CONSTRAINT fkgq03rk0bt1hb915dnyvd3vnfc FOREIGN KEY (revision_id) REFERENCES public."ReservedList"(revision_id); ADD CONSTRAINT fkgq03rk0bt1hb915dnyvd3vnfc FOREIGN KEY (revision_id) REFERENCES public."ReservedList"(revision_id);
--
-- Name: Domain fkjc0r9r5y1lfbt4gpbqw4wsuvq; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Domain"
ADD CONSTRAINT fkjc0r9r5y1lfbt4gpbqw4wsuvq FOREIGN KEY (last_epp_update_client_id) REFERENCES public."Registrar"(client_id);
--
-- Name: Contact fkmb7tdiv85863134w1wogtxrb2; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Contact"
ADD CONSTRAINT fkmb7tdiv85863134w1wogtxrb2 FOREIGN KEY (last_epp_update_client_id) REFERENCES public."Registrar"(client_id);
-- --
-- Name: PremiumEntry fko0gw90lpo1tuee56l0nb6y6g5; Type: FK CONSTRAINT; Schema: public; Owner: - -- Name: PremiumEntry fko0gw90lpo1tuee56l0nb6y6g5; Type: FK CONSTRAINT; Schema: public; Owner: -
-- --