mirror of
https://github.com/google/nomulus.git
synced 2025-05-19 10:49:35 +02:00
Add JPA annotations to class Registrar (#430)
* Add JPA annotations to class Registrar * Use array for Java list * Exclude parent field * Use 3 columns for address and use text for enum * Use EnumParameter and 3 properties in Address * Rename columns and rebase on HEAD
This commit is contained in:
parent
e386bf5bd8
commit
d03cea2443
23 changed files with 827 additions and 46 deletions
|
@ -15,16 +15,22 @@
|
||||||
package google.registry.model.eppcommon;
|
package google.registry.model.eppcommon;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.base.Strings.nullToEmpty;
|
||||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.googlecode.objectify.annotation.Ignore;
|
||||||
|
import com.googlecode.objectify.annotation.OnLoad;
|
||||||
import google.registry.model.Buildable;
|
import google.registry.model.Buildable;
|
||||||
import google.registry.model.ImmutableObject;
|
import google.registry.model.ImmutableObject;
|
||||||
import google.registry.model.JsonMapBuilder;
|
import google.registry.model.JsonMapBuilder;
|
||||||
import google.registry.model.Jsonifiable;
|
import google.registry.model.Jsonifiable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import javax.persistence.Embeddable;
|
||||||
|
import javax.persistence.MappedSuperclass;
|
||||||
|
import javax.persistence.Transient;
|
||||||
import javax.xml.bind.annotation.XmlElement;
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
import javax.xml.bind.annotation.XmlTransient;
|
import javax.xml.bind.annotation.XmlTransient;
|
||||||
import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
|
import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
|
||||||
|
@ -42,12 +48,21 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
* @see google.registry.model.registrar.RegistrarAddress
|
* @see google.registry.model.registrar.RegistrarAddress
|
||||||
*/
|
*/
|
||||||
@XmlTransient
|
@XmlTransient
|
||||||
|
@Embeddable
|
||||||
|
@MappedSuperclass
|
||||||
public class Address extends ImmutableObject implements Jsonifiable {
|
public class Address extends ImmutableObject implements Jsonifiable {
|
||||||
|
|
||||||
/** The schema validation will enforce that this has 3 lines at most. */
|
/** The schema validation will enforce that this has 3 lines at most. */
|
||||||
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
|
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
|
||||||
|
@Transient
|
||||||
List<String> street;
|
List<String> street;
|
||||||
|
|
||||||
|
@Ignore String streetLine1;
|
||||||
|
|
||||||
|
@Ignore String streetLine2;
|
||||||
|
|
||||||
|
@Ignore String streetLine3;
|
||||||
|
|
||||||
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
|
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
|
||||||
String city;
|
String city;
|
||||||
|
|
||||||
|
@ -64,8 +79,24 @@ public class Address extends ImmutableObject implements Jsonifiable {
|
||||||
String countryCode;
|
String countryCode;
|
||||||
|
|
||||||
public ImmutableList<String> getStreet() {
|
public ImmutableList<String> getStreet() {
|
||||||
|
if (street == null && streetLine1 != null) {
|
||||||
|
return ImmutableList.of(streetLine1, nullToEmpty(streetLine2), nullToEmpty(streetLine3));
|
||||||
|
} else {
|
||||||
return nullToEmptyImmutableCopy(street);
|
return nullToEmptyImmutableCopy(street);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStreetLine1() {
|
||||||
|
return streetLine1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStreetLine2() {
|
||||||
|
return streetLine2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStreetLine13() {
|
||||||
|
return streetLine3;
|
||||||
|
}
|
||||||
|
|
||||||
public String getCity() {
|
public String getCity() {
|
||||||
return city;
|
return city;
|
||||||
|
@ -139,4 +170,14 @@ public class Address extends ImmutableObject implements Jsonifiable {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OnLoad
|
||||||
|
void setStreetForCloudSql() {
|
||||||
|
if (street == null || street.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
streetLine1 = street.get(0);
|
||||||
|
streetLine2 = street.size() >= 2 ? street.get(1) : null;
|
||||||
|
streetLine3 = street.size() >= 3 ? street.get(2) : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,8 +169,8 @@ public enum StatusValue implements EppEnum {
|
||||||
/** Hibernate type for sets of {@link StatusValue}. */
|
/** Hibernate type for sets of {@link StatusValue}. */
|
||||||
public static class StatusValueSetType extends EnumSetUserType<StatusValue> {
|
public static class StatusValueSetType extends EnumSetUserType<StatusValue> {
|
||||||
@Override
|
@Override
|
||||||
protected Object convertToElem(Object value) {
|
protected StatusValue convertToElem(String value) {
|
||||||
return StatusValue.valueOf((String) value);
|
return StatusValue.valueOf(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,12 +85,27 @@ import java.util.function.Predicate;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.mail.internet.AddressException;
|
import javax.mail.internet.AddressException;
|
||||||
import javax.mail.internet.InternetAddress;
|
import javax.mail.internet.InternetAddress;
|
||||||
|
import javax.persistence.AttributeOverride;
|
||||||
|
import javax.persistence.AttributeOverrides;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Embedded;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.Transient;
|
||||||
|
import org.hibernate.annotations.Type;
|
||||||
import org.joda.money.CurrencyUnit;
|
import org.joda.money.CurrencyUnit;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
/** Information about a registrar. */
|
/** Information about a registrar. */
|
||||||
@ReportedOn
|
@ReportedOn
|
||||||
@Entity
|
@Entity
|
||||||
|
@javax.persistence.Entity
|
||||||
|
@Table(
|
||||||
|
indexes = {
|
||||||
|
@javax.persistence.Index(columnList = "registrarName", name = "registrar_name_idx"),
|
||||||
|
@javax.persistence.Index(
|
||||||
|
columnList = "ianaIdentifier",
|
||||||
|
name = "registrar_iana_identifier_idx"),
|
||||||
|
})
|
||||||
public class Registrar extends ImmutableObject implements Buildable, Jsonifiable {
|
public class Registrar extends ImmutableObject implements Buildable, Jsonifiable {
|
||||||
|
|
||||||
/** Represents the type of a registrar entity. */
|
/** Represents the type of a registrar entity. */
|
||||||
|
@ -208,14 +223,17 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||||
.doTransactionless(
|
.doTransactionless(
|
||||||
() -> Maps.uniqueIndex(loadAll(), Registrar::getClientId)));
|
() -> Maps.uniqueIndex(loadAll(), Registrar::getClientId)));
|
||||||
|
|
||||||
@Parent
|
@Parent @Transient Key<EntityGroupRoot> parent = getCrossTldKey();
|
||||||
Key<EntityGroupRoot> parent = getCrossTldKey();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unique registrar client id. Must conform to "clIDType" as defined in RFC5730.
|
* Unique registrar client id. Must conform to "clIDType" as defined in RFC5730.
|
||||||
|
*
|
||||||
* @see <a href="http://tools.ietf.org/html/rfc5730#section-4.2">Shared Structure Schema</a>
|
* @see <a href="http://tools.ietf.org/html/rfc5730#section-4.2">Shared Structure Schema</a>
|
||||||
|
* <p>TODO(shicong): Rename this field to clientId
|
||||||
*/
|
*/
|
||||||
@Id
|
@Id
|
||||||
|
@javax.persistence.Id
|
||||||
|
@Column(name = "client_id", nullable = false)
|
||||||
String clientIdentifier;
|
String clientIdentifier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -229,21 +247,27 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||||
* @see <a href="http://www.icann.org/registrar-reports/accredited-list.html">ICANN-Accredited
|
* @see <a href="http://www.icann.org/registrar-reports/accredited-list.html">ICANN-Accredited
|
||||||
* Registrars</a>
|
* Registrars</a>
|
||||||
*/
|
*/
|
||||||
@Index String registrarName;
|
@Index
|
||||||
|
@Column(nullable = false)
|
||||||
|
String registrarName;
|
||||||
|
|
||||||
/** The type of this registrar. */
|
/** The type of this registrar. */
|
||||||
|
@Column(nullable = false)
|
||||||
Type type;
|
Type type;
|
||||||
|
|
||||||
/** The state of this registrar. */
|
/** The state of this registrar. */
|
||||||
State state;
|
State state;
|
||||||
|
|
||||||
/** The set of TLDs which this registrar is allowed to access. */
|
/** The set of TLDs which this registrar is allowed to access. */
|
||||||
|
// TODO(b/147908600): Investigate how to automatically apply user type
|
||||||
|
@org.hibernate.annotations.Type(type = "google.registry.persistence.StringSetUserType")
|
||||||
Set<String> allowedTlds;
|
Set<String> allowedTlds;
|
||||||
|
|
||||||
/** Host name of WHOIS server. */
|
/** Host name of WHOIS server. */
|
||||||
String whoisServer;
|
String whoisServer;
|
||||||
|
|
||||||
/** Base URLs for the registrar's RDAP servers. */
|
/** Base URLs for the registrar's RDAP servers. */
|
||||||
|
@org.hibernate.annotations.Type(type = "google.registry.persistence.StringSetUserType")
|
||||||
Set<String> rdapBaseUrls;
|
Set<String> rdapBaseUrls;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -271,12 +295,14 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||||
String failoverClientCertificateHash;
|
String failoverClientCertificateHash;
|
||||||
|
|
||||||
/** A whitelist of netmasks (in CIDR notation) which the client is allowed to connect from. */
|
/** A whitelist of netmasks (in CIDR notation) which the client is allowed to connect from. */
|
||||||
|
@org.hibernate.annotations.Type(type = "google.registry.persistence.CidrAddressBlockListUserType")
|
||||||
List<CidrAddressBlock> ipAddressWhitelist;
|
List<CidrAddressBlock> ipAddressWhitelist;
|
||||||
|
|
||||||
/** A hashed password for EPP access. The hash is a base64 encoded SHA256 string. */
|
/** A hashed password for EPP access. The hash is a base64 encoded SHA256 string. */
|
||||||
String passwordHash;
|
String passwordHash;
|
||||||
|
|
||||||
/** Randomly generated hash salt. */
|
/** Randomly generated hash salt. */
|
||||||
|
@Column(name = "password_salt")
|
||||||
String salt;
|
String salt;
|
||||||
|
|
||||||
// The following fields may appear redundant to the above, but are
|
// The following fields may appear redundant to the above, but are
|
||||||
|
@ -287,6 +313,24 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||||
* unrestricted UTF-8.
|
* unrestricted UTF-8.
|
||||||
*/
|
*/
|
||||||
@IgnoreSave(IfNull.class)
|
@IgnoreSave(IfNull.class)
|
||||||
|
@Embedded
|
||||||
|
@AttributeOverrides({
|
||||||
|
@AttributeOverride(
|
||||||
|
name = "streetLine1",
|
||||||
|
column = @Column(name = "localized_address_street_line1")),
|
||||||
|
@AttributeOverride(
|
||||||
|
name = "streetLine2",
|
||||||
|
column = @Column(name = "localized_address_street_line2")),
|
||||||
|
@AttributeOverride(
|
||||||
|
name = "streetLine3",
|
||||||
|
column = @Column(name = "localized_address_street_line3")),
|
||||||
|
@AttributeOverride(name = "city", column = @Column(name = "localized_address_city")),
|
||||||
|
@AttributeOverride(name = "state", column = @Column(name = "localized_address_state")),
|
||||||
|
@AttributeOverride(name = "zip", column = @Column(name = "localized_address_zip")),
|
||||||
|
@AttributeOverride(
|
||||||
|
name = "countryCode",
|
||||||
|
column = @Column(name = "localized_address_country_code"))
|
||||||
|
})
|
||||||
RegistrarAddress localizedAddress;
|
RegistrarAddress localizedAddress;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -294,6 +338,16 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||||
* representable in the 7-bit US-ASCII character set.
|
* representable in the 7-bit US-ASCII character set.
|
||||||
*/
|
*/
|
||||||
@IgnoreSave(IfNull.class)
|
@IgnoreSave(IfNull.class)
|
||||||
|
@Embedded
|
||||||
|
@AttributeOverrides({
|
||||||
|
@AttributeOverride(name = "streetLine1", column = @Column(name = "i18n_address_street_line1")),
|
||||||
|
@AttributeOverride(name = "streetLine2", column = @Column(name = "i18n_address_street_line2")),
|
||||||
|
@AttributeOverride(name = "streetLine3", column = @Column(name = "i18n_address_street_line3")),
|
||||||
|
@AttributeOverride(name = "city", column = @Column(name = "i18n_address_city")),
|
||||||
|
@AttributeOverride(name = "state", column = @Column(name = "i18n_address_state")),
|
||||||
|
@AttributeOverride(name = "zip", column = @Column(name = "i18n_address_zip")),
|
||||||
|
@AttributeOverride(name = "countryCode", column = @Column(name = "i18n_address_country_code"))
|
||||||
|
})
|
||||||
RegistrarAddress internationalizedAddress;
|
RegistrarAddress internationalizedAddress;
|
||||||
|
|
||||||
/** Voice number. */
|
/** Voice number. */
|
||||||
|
@ -309,16 +363,17 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registrar identifier used for reporting to ICANN.
|
* Registrar identifier used for reporting to ICANN.
|
||||||
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>8 is used for Testing Registrar.
|
* <li>8 is used for Testing Registrar.
|
||||||
* <li>9997 is used by ICAAN for SLA monitoring.
|
* <li>9997 is used by ICAAN for SLA monitoring.
|
||||||
* <li>9999 is used for cases when the registry operator acts as registrar.
|
* <li>9999 is used for cases when the registry operator acts as registrar.
|
||||||
* </ul>
|
* </ul>
|
||||||
* @see <a href="http://www.iana.org/assignments/registrar-ids/registrar-ids.txt">Registrar IDs</a>
|
*
|
||||||
|
* @see <a href="http://www.iana.org/assignments/registrar-ids/registrar-ids.txt">Registrar
|
||||||
|
* IDs</a>
|
||||||
*/
|
*/
|
||||||
@Index
|
@Index @Nullable Long ianaIdentifier;
|
||||||
@Nullable
|
|
||||||
Long ianaIdentifier;
|
|
||||||
|
|
||||||
/** Identifier of registrar used in external billing system (e.g. Oracle). */
|
/** Identifier of registrar used in external billing system (e.g. Oracle). */
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -338,18 +393,19 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
@Mapify(CurrencyMapper.class)
|
@Mapify(CurrencyMapper.class)
|
||||||
|
@org.hibernate.annotations.Type(type = "google.registry.persistence.CurrencyToBillingMapUserType")
|
||||||
Map<CurrencyUnit, BillingAccountEntry> billingAccountMap;
|
Map<CurrencyUnit, BillingAccountEntry> billingAccountMap;
|
||||||
|
|
||||||
/** A billing account entry for this registrar, consisting of a currency and an account Id. */
|
/** A billing account entry for this registrar, consisting of a currency and an account Id. */
|
||||||
@Embed
|
@Embed
|
||||||
static class BillingAccountEntry extends ImmutableObject {
|
public static class BillingAccountEntry extends ImmutableObject {
|
||||||
|
|
||||||
CurrencyUnit currency;
|
CurrencyUnit currency;
|
||||||
String accountId;
|
String accountId;
|
||||||
|
|
||||||
BillingAccountEntry() {}
|
BillingAccountEntry() {}
|
||||||
|
|
||||||
BillingAccountEntry(CurrencyUnit currency, String accountId) {
|
public BillingAccountEntry(CurrencyUnit currency, String accountId) {
|
||||||
this.accountId = accountId;
|
this.accountId = accountId;
|
||||||
this.currency = currency;
|
this.currency = currency;
|
||||||
}
|
}
|
||||||
|
@ -366,6 +422,11 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||||
return billingAccountEntry.currency;
|
return billingAccountEntry.currency;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the account id of this entry. */
|
||||||
|
public String getAccountId() {
|
||||||
|
return accountId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** URL of registrar's website. */
|
/** URL of registrar's website. */
|
||||||
|
@ -390,14 +451,10 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||||
/** An automatically managed last-saved timestamp. */
|
/** An automatically managed last-saved timestamp. */
|
||||||
UpdateAutoTimestamp lastUpdateTime = UpdateAutoTimestamp.create(null);
|
UpdateAutoTimestamp lastUpdateTime = UpdateAutoTimestamp.create(null);
|
||||||
|
|
||||||
/**
|
/** The time that the certificate was last updated. */
|
||||||
* The time that the certificate was last updated.
|
|
||||||
*/
|
|
||||||
DateTime lastCertificateUpdateTime;
|
DateTime lastCertificateUpdateTime;
|
||||||
|
|
||||||
/**
|
/** Telephone support passcode (5-digit numeric) */
|
||||||
* Telephone support passcode (5-digit numeric)
|
|
||||||
*/
|
|
||||||
String phonePasscode;
|
String phonePasscode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,6 +20,7 @@ import static google.registry.util.CollectionUtils.forceEmptyToNull;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registrar Address
|
* Registrar Address
|
||||||
|
@ -29,6 +30,7 @@ import google.registry.model.eppcommon.Address;
|
||||||
* classes.
|
* classes.
|
||||||
*/
|
*/
|
||||||
@Embed
|
@Embed
|
||||||
|
@Embeddable
|
||||||
public class RegistrarAddress extends Address {
|
public class RegistrarAddress extends Address {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.persistence;
|
||||||
|
|
||||||
|
import google.registry.util.CidrAddressBlock;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hibernate {@link org.hibernate.usertype.UserType} for storing/retrieving {@link
|
||||||
|
* List<CidrAddressBlock>} objects.
|
||||||
|
*/
|
||||||
|
public class CidrAddressBlockListUserType extends StringListUserType<CidrAddressBlock> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CidrAddressBlock convertToElem(String columnValue) {
|
||||||
|
return columnValue == null ? null : CidrAddressBlock.create(columnValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String convertToColumn(CidrAddressBlock elementValue) {
|
||||||
|
return elementValue == null ? null : elementValue.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.persistence;
|
||||||
|
|
||||||
|
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||||
|
|
||||||
|
import google.registry.model.registrar.Registrar.BillingAccountEntry;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.hibernate.usertype.UserType;
|
||||||
|
import org.joda.money.CurrencyUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom {@link UserType} for storing/retrieving {@link Map<CurrencyUnit, BillingAccountEntry>}
|
||||||
|
* objects.
|
||||||
|
*/
|
||||||
|
public class CurrencyToBillingMapUserType extends MapUserType {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object toEntityTypeMap(Map<String, String> map) {
|
||||||
|
return map.entrySet().stream()
|
||||||
|
.collect(
|
||||||
|
toImmutableMap(
|
||||||
|
entry -> CurrencyUnit.of(entry.getKey()),
|
||||||
|
entry ->
|
||||||
|
new BillingAccountEntry(CurrencyUnit.of(entry.getKey()), entry.getValue())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> toDbSupportedMap(Object map) {
|
||||||
|
return ((Map<CurrencyUnit, BillingAccountEntry>) map)
|
||||||
|
.entrySet().stream()
|
||||||
|
.collect(
|
||||||
|
toImmutableMap(
|
||||||
|
entry -> entry.getKey().getCode(), entry -> entry.getValue().getAccountId()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,11 +18,12 @@ import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/** Abstract Hibernate user type for storing/retrieving {@link Set<Enum<E>>}. */
|
/** Abstract Hibernate user type for storing/retrieving {@link Set<Enum<E>>}. */
|
||||||
public class EnumSetUserType<E extends Enum<E>> extends GenericCollectionUserType<Set<Enum<E>>> {
|
public class EnumSetUserType<E extends Enum<E>>
|
||||||
|
extends GenericCollectionUserType<Set<E>, E, String> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Set<Enum<E>> getNewCollection() {
|
Set<E> getNewCollection() {
|
||||||
return new HashSet<Enum<E>>();
|
return new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
package google.registry.persistence;
|
package google.registry.persistence;
|
||||||
|
|
||||||
|
import google.registry.util.TypeUtils.TypeInstantiator;
|
||||||
import java.sql.Array;
|
import java.sql.Array;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
|
@ -23,8 +24,15 @@ import java.util.Collection;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
|
||||||
/** Generic Hibernate user type to store/retrieve Java collection as an array in Cloud SQL. */
|
/**
|
||||||
public abstract class GenericCollectionUserType<T extends Collection> extends MutableUserType {
|
* Generic Hibernate user type to store/retrieve Java collection as an array in Cloud SQL.
|
||||||
|
*
|
||||||
|
* @param <T> the concrete {@link Collection} type of the entity field
|
||||||
|
* @param <E> the Java type of the element for the collection of the entity field
|
||||||
|
* @param <C> the JDBC supported type of the element in the DB column array
|
||||||
|
*/
|
||||||
|
public abstract class GenericCollectionUserType<T extends Collection<E>, E, C>
|
||||||
|
extends MutableUserType {
|
||||||
|
|
||||||
abstract T getNewCollection();
|
abstract T getNewCollection();
|
||||||
|
|
||||||
|
@ -54,6 +62,11 @@ public abstract class GenericCollectionUserType<T extends Collection> extends Mu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class returnedClass() {
|
||||||
|
return new TypeInstantiator<T>(getClass()) {}.getExactType();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int[] sqlTypes() {
|
public int[] sqlTypes() {
|
||||||
return new int[] {getColumnType().getTypeCode()};
|
return new int[] {getColumnType().getTypeCode()};
|
||||||
|
@ -65,7 +78,7 @@ public abstract class GenericCollectionUserType<T extends Collection> extends Mu
|
||||||
throws HibernateException, SQLException {
|
throws HibernateException, SQLException {
|
||||||
if (rs.getArray(names[0]) != null) {
|
if (rs.getArray(names[0]) != null) {
|
||||||
T result = getNewCollection();
|
T result = getNewCollection();
|
||||||
for (Object element : (Object[]) rs.getArray(names[0]).getArray()) {
|
for (C element : (C[]) rs.getArray(names[0]).getArray()) {
|
||||||
result.add(convertToElem(element));
|
result.add(convertToElem(element));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -81,8 +94,12 @@ public abstract class GenericCollectionUserType<T extends Collection> extends Mu
|
||||||
st.setArray(index, null);
|
st.setArray(index, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
T list = (T) value;
|
T collection = (T) value;
|
||||||
Array arr = st.getConnection().createArrayOf(getColumnType().getTypeName(), list.toArray());
|
Array arr =
|
||||||
|
st.getConnection()
|
||||||
|
.createArrayOf(
|
||||||
|
getColumnType().getTypeName(),
|
||||||
|
collection.stream().map(this::convertToColumn).toArray());
|
||||||
st.setArray(index, arr);
|
st.setArray(index, arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +109,11 @@ public abstract class GenericCollectionUserType<T extends Collection> extends Mu
|
||||||
* <p>This method is useful when encoding a java type to one of the types that can be used as an
|
* <p>This method is useful when encoding a java type to one of the types that can be used as an
|
||||||
* array element.
|
* array element.
|
||||||
*/
|
*/
|
||||||
protected Object convertToElem(Object columnValue) {
|
protected E convertToElem(C columnValue) {
|
||||||
return columnValue;
|
return (E) columnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected C convertToColumn(E elementValue) {
|
||||||
|
return (C) elementValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.persistence;
|
||||||
|
|
||||||
|
import google.registry.util.TypeUtils.TypeInstantiator;
|
||||||
|
import javax.persistence.AttributeConverter;
|
||||||
|
|
||||||
|
/** Generic converter for storing/retrieving {@link Enum} objects. */
|
||||||
|
public class GenericEnumConverter<T extends Enum<T>> implements AttributeConverter<T, String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String convertToDatabaseColumn(T attribute) {
|
||||||
|
return attribute == null ? null : attribute.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T convertToEntityAttribute(String dbData) {
|
||||||
|
return dbData == null
|
||||||
|
? null
|
||||||
|
: Enum.valueOf(new TypeInstantiator<T>(getClass()) {}.getExactType(), dbData);
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,13 +45,29 @@ public class MapUserType extends MutableUserType {
|
||||||
public Object nullSafeGet(
|
public Object nullSafeGet(
|
||||||
ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
|
ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
|
||||||
throws HibernateException, SQLException {
|
throws HibernateException, SQLException {
|
||||||
return rs.getObject(names[0]);
|
return toEntityTypeMap((Map<String, String>) rs.getObject(names[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nullSafeSet(
|
public void nullSafeSet(
|
||||||
PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
|
PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
|
||||||
throws HibernateException, SQLException {
|
throws HibernateException, SQLException {
|
||||||
st.setObject(index, value);
|
st.setObject(index, toDbSupportedMap(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclass can override this method to convert the {@link Map<String, String>} to a {@link Map}
|
||||||
|
* of specific type defined in the entity class.
|
||||||
|
*/
|
||||||
|
public Object toEntityTypeMap(Map<String, String> map) {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclass can override this method to convert the {@link Map} of specific type to a {@link
|
||||||
|
* Map<String, String>} that can be stored in the hstore type column.
|
||||||
|
*/
|
||||||
|
public Map<String, String> toDbSupportedMap(Object map) {
|
||||||
|
return (Map<String, String>) map;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.persistence;
|
||||||
|
|
||||||
|
import google.registry.model.registrar.Registrar;
|
||||||
|
import javax.persistence.Converter;
|
||||||
|
|
||||||
|
/** JPA converter for storing/retrieving {@link Registrar.State} objects. */
|
||||||
|
@Converter(autoApply = true)
|
||||||
|
public class RegistrarStateConverter extends GenericEnumConverter<Registrar.State> {}
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.persistence;
|
||||||
|
|
||||||
|
import google.registry.model.registrar.Registrar;
|
||||||
|
import javax.persistence.Converter;
|
||||||
|
|
||||||
|
/** JPA converter for storing/retrieving {@link Registrar.Type} objects. */
|
||||||
|
@Converter(autoApply = true)
|
||||||
|
public class RegistrarTypeConverter extends GenericEnumConverter<Registrar.Type> {}
|
|
@ -18,10 +18,10 @@ import com.google.common.collect.Lists;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** Abstract Hibernate user type for storing/retrieving {@link List<String>}. */
|
/** Abstract Hibernate user type for storing/retrieving {@link List<String>}. */
|
||||||
public class StringListUserType extends GenericCollectionUserType<List<String>> {
|
public class StringListUserType<E> extends GenericCollectionUserType<List<E>, E, String> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
List<String> getNewCollection() {
|
List<E> getNewCollection() {
|
||||||
return Lists.newArrayList();
|
return Lists.newArrayList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,9 +29,4 @@ public class StringListUserType extends GenericCollectionUserType<List<String>>
|
||||||
ArrayColumnType getColumnType() {
|
ArrayColumnType getColumnType() {
|
||||||
return ArrayColumnType.STRING;
|
return ArrayColumnType.STRING;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class returnedClass() {
|
|
||||||
return List.class;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,10 @@ import com.google.common.collect.Sets;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/** Abstract Hibernate user type for storing/retrieving {@link Set<String>}. */
|
/** Abstract Hibernate user type for storing/retrieving {@link Set<String>}. */
|
||||||
public class StringSetUserType extends GenericCollectionUserType<Set<String>> {
|
public class StringSetUserType<E> extends GenericCollectionUserType<Set<E>, E, String> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Set<String> getNewCollection() {
|
Set<E> getNewCollection() {
|
||||||
return Sets.newHashSet();
|
return Sets.newHashSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,9 +29,4 @@ public class StringSetUserType extends GenericCollectionUserType<Set<String>> {
|
||||||
ArrayColumnType getColumnType() {
|
ArrayColumnType getColumnType() {
|
||||||
return ArrayColumnType.STRING;
|
return ArrayColumnType.STRING;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class returnedClass() {
|
|
||||||
return Set.class;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
* Use Hibernate's ServiceRegistry for bootstrapping (not JPA-compliant)
|
* Use Hibernate's ServiceRegistry for bootstrapping (not JPA-compliant)
|
||||||
-->
|
-->
|
||||||
<class>google.registry.model.domain.DomainBase</class>
|
<class>google.registry.model.domain.DomainBase</class>
|
||||||
|
<class>google.registry.model.registrar.Registrar</class>
|
||||||
<class>google.registry.schema.domain.RegistryLock</class>
|
<class>google.registry.schema.domain.RegistryLock</class>
|
||||||
<class>google.registry.schema.tmch.ClaimsList</class>
|
<class>google.registry.schema.tmch.ClaimsList</class>
|
||||||
<class>google.registry.schema.cursor.Cursor</class>
|
<class>google.registry.schema.cursor.Cursor</class>
|
||||||
|
@ -40,6 +41,8 @@
|
||||||
<class>google.registry.persistence.CreateAutoTimestampConverter</class>
|
<class>google.registry.persistence.CreateAutoTimestampConverter</class>
|
||||||
<class>google.registry.persistence.CurrencyUnitConverter</class>
|
<class>google.registry.persistence.CurrencyUnitConverter</class>
|
||||||
<class>google.registry.persistence.DateTimeConverter</class>
|
<class>google.registry.persistence.DateTimeConverter</class>
|
||||||
|
<class>google.registry.persistence.RegistrarStateConverter</class>
|
||||||
|
<class>google.registry.persistence.RegistrarTypeConverter</class>
|
||||||
<class>google.registry.persistence.UpdateAutoTimestampConverter</class>
|
<class>google.registry.persistence.UpdateAutoTimestampConverter</class>
|
||||||
<class>google.registry.persistence.ZonedDateTimeConverter</class>
|
<class>google.registry.persistence.ZonedDateTimeConverter</class>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.persistence;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import google.registry.model.ImmutableObject;
|
||||||
|
import google.registry.persistence.transaction.JpaTestRules;
|
||||||
|
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
|
||||||
|
import google.registry.util.CidrAddressBlock;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import org.hibernate.annotations.Type;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
/** Unit tests for {@link CidrAddressBlockListUserType}. */
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public class CidrAddressBlockListUserTypeTest {
|
||||||
|
@Rule
|
||||||
|
public final JpaUnitTestRule jpaRule =
|
||||||
|
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void roundTripConversion_returnsSameCidrAddressBlock() {
|
||||||
|
List<CidrAddressBlock> addresses =
|
||||||
|
ImmutableList.of(
|
||||||
|
CidrAddressBlock.create("0.0.0.0/32"),
|
||||||
|
CidrAddressBlock.create("255.255.255.254/31"),
|
||||||
|
CidrAddressBlock.create("::"),
|
||||||
|
CidrAddressBlock.create("8000::/1"),
|
||||||
|
CidrAddressBlock.create("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
|
||||||
|
TestEntity testEntity = new TestEntity(addresses);
|
||||||
|
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
|
||||||
|
TestEntity persisted =
|
||||||
|
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id"));
|
||||||
|
assertThat(persisted.addresses).isEqualTo(addresses);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
|
||||||
|
private static class TestEntity extends ImmutableObject {
|
||||||
|
|
||||||
|
@Id String name = "id";
|
||||||
|
|
||||||
|
@Type(type = "google.registry.persistence.CidrAddressBlockListUserType")
|
||||||
|
List<CidrAddressBlock> addresses;
|
||||||
|
|
||||||
|
private TestEntity() {}
|
||||||
|
|
||||||
|
private TestEntity(List<CidrAddressBlock> addresses) {
|
||||||
|
this.addresses = addresses;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.persistence;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import google.registry.model.ImmutableObject;
|
||||||
|
import google.registry.model.registrar.Registrar.BillingAccountEntry;
|
||||||
|
import google.registry.persistence.transaction.JpaTestRules;
|
||||||
|
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import org.hibernate.annotations.Type;
|
||||||
|
import org.joda.money.CurrencyUnit;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
/** Unit tests for {@link CurrencyToBillingMapUserType}. */
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public class CurrencyToBillingMapUserTypeTest {
|
||||||
|
@Rule
|
||||||
|
public final JpaUnitTestRule jpaRule =
|
||||||
|
new JpaTestRules.Builder()
|
||||||
|
.withInitScript("sql/flyway/V14__load_extension_for_hstore.sql")
|
||||||
|
.withEntityClass(TestEntity.class)
|
||||||
|
.buildUnitTestRule();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void roundTripConversion_returnsSameCurrencyToBillingMap() {
|
||||||
|
ImmutableMap<CurrencyUnit, BillingAccountEntry> currencyToBilling =
|
||||||
|
ImmutableMap.of(
|
||||||
|
CurrencyUnit.of("USD"),
|
||||||
|
new BillingAccountEntry(CurrencyUnit.of("USD"), "accountId1"),
|
||||||
|
CurrencyUnit.of("CNY"),
|
||||||
|
new BillingAccountEntry(CurrencyUnit.of("CNY"), "accountId2"));
|
||||||
|
TestEntity testEntity = new TestEntity(currencyToBilling);
|
||||||
|
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
|
||||||
|
TestEntity persisted =
|
||||||
|
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id"));
|
||||||
|
assertThat(persisted.currencyToBilling).containsExactlyEntriesIn(currencyToBilling);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
|
||||||
|
private static class TestEntity extends ImmutableObject {
|
||||||
|
|
||||||
|
@Id String name = "id";
|
||||||
|
|
||||||
|
@Type(type = "google.registry.persistence.CurrencyToBillingMapUserType")
|
||||||
|
Map<CurrencyUnit, BillingAccountEntry> currencyToBilling;
|
||||||
|
|
||||||
|
private TestEntity() {}
|
||||||
|
|
||||||
|
private TestEntity(Map<CurrencyUnit, BillingAccountEntry> currencyToBilling) {
|
||||||
|
this.currencyToBilling = currencyToBilling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,8 +56,8 @@ public class EnumSetUserTypeTest {
|
||||||
|
|
||||||
public static class TestEnumType extends EnumSetUserType<TestEnum> {
|
public static class TestEnumType extends EnumSetUserType<TestEnum> {
|
||||||
@Override
|
@Override
|
||||||
protected Object convertToElem(Object value) {
|
protected TestEnum convertToElem(String value) {
|
||||||
return TestEnum.valueOf((String) value);
|
return TestEnum.valueOf(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.persistence;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||||
|
|
||||||
|
import google.registry.model.ImmutableObject;
|
||||||
|
import google.registry.model.registrar.Registrar.State;
|
||||||
|
import google.registry.persistence.transaction.JpaTestRules;
|
||||||
|
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
/** Unit tests for {@link RegistrarStateConverter}. */
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public class RegistrarStateConverterTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final JpaUnitTestRule jpaRule =
|
||||||
|
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void roundTripConversion_returnsSameEnum() {
|
||||||
|
TestEntity testEntity = new TestEntity(State.ACTIVE);
|
||||||
|
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
|
||||||
|
TestEntity persisted =
|
||||||
|
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id"));
|
||||||
|
assertThat(persisted.state).isEqualTo(State.ACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNativeQuery_succeeds() {
|
||||||
|
TestEntity testEntity = new TestEntity(State.DISABLED);
|
||||||
|
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
jpaTm()
|
||||||
|
.transact(
|
||||||
|
() ->
|
||||||
|
jpaTm()
|
||||||
|
.getEntityManager()
|
||||||
|
.createNativeQuery("SELECT state FROM \"TestEntity\" WHERE name = 'id'")
|
||||||
|
.getSingleResult()))
|
||||||
|
.isEqualTo("DISABLED");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
|
||||||
|
private static class TestEntity extends ImmutableObject {
|
||||||
|
|
||||||
|
@Id String name = "id";
|
||||||
|
|
||||||
|
State state;
|
||||||
|
|
||||||
|
private TestEntity() {}
|
||||||
|
|
||||||
|
private TestEntity(State state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.persistence;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||||
|
|
||||||
|
import google.registry.model.ImmutableObject;
|
||||||
|
import google.registry.model.registrar.Registrar.Type;
|
||||||
|
import google.registry.persistence.transaction.JpaTestRules;
|
||||||
|
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
/** Unit tests for {@link RegistrarTypeConverter}. */
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public class RegistrarTypeConverterTest {
|
||||||
|
@Rule
|
||||||
|
public final JpaUnitTestRule jpaRule =
|
||||||
|
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void roundTripConversion_returnsSameEnum() {
|
||||||
|
TestEntity testEntity = new TestEntity(Type.MONITORING);
|
||||||
|
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
|
||||||
|
TestEntity persisted =
|
||||||
|
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id"));
|
||||||
|
assertThat(persisted.type).isEqualTo(Type.MONITORING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNativeQuery_succeeds() {
|
||||||
|
TestEntity testEntity = new TestEntity(Type.MONITORING);
|
||||||
|
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
jpaTm()
|
||||||
|
.transact(
|
||||||
|
() ->
|
||||||
|
jpaTm()
|
||||||
|
.getEntityManager()
|
||||||
|
.createNativeQuery("SELECT type FROM \"TestEntity\" WHERE name = 'id'")
|
||||||
|
.getSingleResult()))
|
||||||
|
.isEqualTo("MONITORING");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
|
||||||
|
private static class TestEntity extends ImmutableObject {
|
||||||
|
|
||||||
|
@Id String name = "id";
|
||||||
|
|
||||||
|
Type type;
|
||||||
|
|
||||||
|
private TestEntity() {}
|
||||||
|
|
||||||
|
private TestEntity(Type type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
db/src/main/resources/sql/flyway/V16__create_registrar.sql
Normal file
65
db/src/main/resources/sql/flyway/V16__create_registrar.sql
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
-- 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 "Registrar" (
|
||||||
|
client_id text not null,
|
||||||
|
allowed_tlds text[],
|
||||||
|
billing_account_map hstore,
|
||||||
|
billing_identifier int8,
|
||||||
|
block_premium_names boolean not null,
|
||||||
|
client_certificate text,
|
||||||
|
client_certificate_hash text,
|
||||||
|
contacts_require_syncing boolean not null,
|
||||||
|
creation_time timestamptz,
|
||||||
|
drive_folder_id text,
|
||||||
|
email_address text,
|
||||||
|
failover_client_certificate text,
|
||||||
|
failover_client_certificate_hash text,
|
||||||
|
fax_number text,
|
||||||
|
iana_identifier int8,
|
||||||
|
icann_referral_email text,
|
||||||
|
i18n_address_city text,
|
||||||
|
i18n_address_country_code text,
|
||||||
|
i18n_address_state text,
|
||||||
|
i18n_address_street_line1 text,
|
||||||
|
i18n_address_street_line2 text,
|
||||||
|
i18n_address_street_line3 text,
|
||||||
|
i18n_address_zip text,
|
||||||
|
ip_address_whitelist text[],
|
||||||
|
last_certificate_update_time timestamptz,
|
||||||
|
last_update_time timestamptz,
|
||||||
|
localized_address_city text,
|
||||||
|
localized_address_country_code text,
|
||||||
|
localized_address_state text,
|
||||||
|
localized_address_street_line1 text,
|
||||||
|
localized_address_street_line2 text,
|
||||||
|
localized_address_street_line3 text,
|
||||||
|
localized_address_zip text,
|
||||||
|
password_hash text,
|
||||||
|
phone_number text,
|
||||||
|
phone_passcode text,
|
||||||
|
po_number text,
|
||||||
|
rdap_base_urls text[],
|
||||||
|
registrar_name text not null,
|
||||||
|
registry_lock_allowed boolean not null,
|
||||||
|
password_salt text,
|
||||||
|
state text,
|
||||||
|
type text not null,
|
||||||
|
url text,
|
||||||
|
whois_server text,
|
||||||
|
primary key (client_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
create index registrar_name_idx on "Registrar" (registrar_name);
|
||||||
|
create index registrar_iana_identifier_idx on "Registrar" (iana_identifier);
|
|
@ -93,6 +93,55 @@
|
||||||
primary key (revision_id)
|
primary key (revision_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create table "Registrar" (
|
||||||
|
client_id text not null,
|
||||||
|
allowed_tlds text[],
|
||||||
|
billing_account_map hstore,
|
||||||
|
billing_identifier int8,
|
||||||
|
block_premium_names boolean not null,
|
||||||
|
client_certificate text,
|
||||||
|
client_certificate_hash text,
|
||||||
|
contacts_require_syncing boolean not null,
|
||||||
|
creation_time timestamptz,
|
||||||
|
drive_folder_id text,
|
||||||
|
email_address text,
|
||||||
|
failover_client_certificate text,
|
||||||
|
failover_client_certificate_hash text,
|
||||||
|
fax_number text,
|
||||||
|
iana_identifier int8,
|
||||||
|
icann_referral_email text,
|
||||||
|
i18n_address_city text,
|
||||||
|
i18n_address_country_code text,
|
||||||
|
i18n_address_state text,
|
||||||
|
i18n_address_street_line1 text,
|
||||||
|
i18n_address_street_line2 text,
|
||||||
|
i18n_address_street_line3 text,
|
||||||
|
i18n_address_zip text,
|
||||||
|
ip_address_whitelist text[],
|
||||||
|
last_certificate_update_time timestamptz,
|
||||||
|
last_update_time timestamptz,
|
||||||
|
localized_address_city text,
|
||||||
|
localized_address_country_code text,
|
||||||
|
localized_address_state text,
|
||||||
|
localized_address_street_line1 text,
|
||||||
|
localized_address_street_line2 text,
|
||||||
|
localized_address_street_line3 text,
|
||||||
|
localized_address_zip text,
|
||||||
|
password_hash text,
|
||||||
|
phone_number text,
|
||||||
|
phone_passcode text,
|
||||||
|
po_number text,
|
||||||
|
rdap_base_urls text[],
|
||||||
|
registrar_name text not null,
|
||||||
|
registry_lock_allowed boolean not null,
|
||||||
|
password_salt text,
|
||||||
|
state text,
|
||||||
|
type text not null,
|
||||||
|
url text,
|
||||||
|
whois_server text,
|
||||||
|
primary key (client_id)
|
||||||
|
);
|
||||||
|
|
||||||
create table "RegistryLock" (
|
create table "RegistryLock" (
|
||||||
revision_id bigserial not null,
|
revision_id bigserial not null,
|
||||||
domain_name text not null,
|
domain_name text not null,
|
||||||
|
@ -130,6 +179,8 @@ create index IDX5mnf0wn20tno4b9do88j61klr on "Domain" (deletion_time);
|
||||||
create index IDX1rcgkdd777bpvj0r94sltwd5y on "Domain" (fully_qualified_domain_name);
|
create index IDX1rcgkdd777bpvj0r94sltwd5y on "Domain" (fully_qualified_domain_name);
|
||||||
create index IDXrwl38wwkli1j7gkvtywi9jokq on "Domain" (tld);
|
create index IDXrwl38wwkli1j7gkvtywi9jokq on "Domain" (tld);
|
||||||
create index premiumlist_name_idx on "PremiumList" (name);
|
create index premiumlist_name_idx on "PremiumList" (name);
|
||||||
|
create index registrar_name_idx on "Registrar" (registrar_name);
|
||||||
|
create index registrar_iana_identifier_idx on "Registrar" (iana_identifier);
|
||||||
create index idx_registry_lock_verification_code on "RegistryLock" (verification_code);
|
create index idx_registry_lock_verification_code on "RegistryLock" (verification_code);
|
||||||
create index idx_registry_lock_registrar_id on "RegistryLock" (registrar_id);
|
create index idx_registry_lock_registrar_id on "RegistryLock" (registrar_id);
|
||||||
|
|
||||||
|
|
|
@ -159,6 +159,59 @@ CREATE SEQUENCE public."PremiumList_revision_id_seq"
|
||||||
ALTER SEQUENCE public."PremiumList_revision_id_seq" OWNED BY public."PremiumList".revision_id;
|
ALTER SEQUENCE public."PremiumList_revision_id_seq" OWNED BY public."PremiumList".revision_id;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: Registrar; Type: TABLE; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE public."Registrar" (
|
||||||
|
client_id text NOT NULL,
|
||||||
|
allowed_tlds text[],
|
||||||
|
billing_account_map public.hstore,
|
||||||
|
billing_identifier bigint,
|
||||||
|
block_premium_names boolean NOT NULL,
|
||||||
|
client_certificate text,
|
||||||
|
client_certificate_hash text,
|
||||||
|
contacts_require_syncing boolean NOT NULL,
|
||||||
|
creation_time timestamp with time zone,
|
||||||
|
drive_folder_id text,
|
||||||
|
email_address text,
|
||||||
|
failover_client_certificate text,
|
||||||
|
failover_client_certificate_hash text,
|
||||||
|
fax_number text,
|
||||||
|
iana_identifier bigint,
|
||||||
|
icann_referral_email text,
|
||||||
|
i18n_address_city text,
|
||||||
|
i18n_address_country_code text,
|
||||||
|
i18n_address_state text,
|
||||||
|
i18n_address_street_line1 text,
|
||||||
|
i18n_address_street_line2 text,
|
||||||
|
i18n_address_street_line3 text,
|
||||||
|
i18n_address_zip text,
|
||||||
|
ip_address_whitelist text[],
|
||||||
|
last_certificate_update_time timestamp with time zone,
|
||||||
|
last_update_time timestamp with time zone,
|
||||||
|
localized_address_city text,
|
||||||
|
localized_address_country_code text,
|
||||||
|
localized_address_state text,
|
||||||
|
localized_address_street_line1 text,
|
||||||
|
localized_address_street_line2 text,
|
||||||
|
localized_address_street_line3 text,
|
||||||
|
localized_address_zip text,
|
||||||
|
password_hash text,
|
||||||
|
phone_number text,
|
||||||
|
phone_passcode text,
|
||||||
|
po_number text,
|
||||||
|
rdap_base_urls text[],
|
||||||
|
registrar_name text NOT NULL,
|
||||||
|
registry_lock_allowed boolean NOT NULL,
|
||||||
|
password_salt text,
|
||||||
|
state text,
|
||||||
|
type text NOT NULL,
|
||||||
|
url text,
|
||||||
|
whois_server text
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: RegistryLock; Type: TABLE; Schema: public; Owner: -
|
-- Name: RegistryLock; Type: TABLE; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
@ -317,6 +370,14 @@ ALTER TABLE ONLY public."PremiumList"
|
||||||
ADD CONSTRAINT "PremiumList_pkey" PRIMARY KEY (revision_id);
|
ADD CONSTRAINT "PremiumList_pkey" PRIMARY KEY (revision_id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: Registrar Registrar_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public."Registrar"
|
||||||
|
ADD CONSTRAINT "Registrar_pkey" PRIMARY KEY (client_id);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: RegistryLock RegistryLock_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
-- Name: RegistryLock RegistryLock_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
@ -405,6 +466,20 @@ CREATE INDEX idxrwl38wwkli1j7gkvtywi9jokq ON public."Domain" USING btree (tld);
|
||||||
CREATE INDEX premiumlist_name_idx ON public."PremiumList" USING btree (name);
|
CREATE INDEX premiumlist_name_idx ON public."PremiumList" USING btree (name);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: registrar_iana_identifier_idx; Type: INDEX; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE INDEX registrar_iana_identifier_idx ON public."Registrar" USING btree (iana_identifier);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: registrar_name_idx; Type: INDEX; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE INDEX registrar_name_idx ON public."Registrar" USING btree (registrar_name);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: reservedlist_name_idx; Type: INDEX; Schema: public; Owner: -
|
-- Name: reservedlist_name_idx; Type: INDEX; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue