diff --git a/core/src/main/java/google/registry/flows/domain/DomainPricingLogic.java b/core/src/main/java/google/registry/flows/domain/DomainPricingLogic.java
index 9a9f32895..368c0eb5f 100644
--- a/core/src/main/java/google/registry/flows/domain/DomainPricingLogic.java
+++ b/core/src/main/java/google/registry/flows/domain/DomainPricingLogic.java
@@ -192,7 +192,7 @@ public final class DomainPricingLogic {
new FeesAndCredits.Builder()
.setCurrency(tld.getCurrency())
.addFeeOrCredit(
- Fee.create(tld.getStandardRestoreCost().getAmount(), FeeType.RESTORE, false));
+ Fee.create(tld.getRestoreBillingCost().getAmount(), FeeType.RESTORE, false));
if (isExpired) {
feesAndCredits.addFeeOrCredit(
Fee.create(
diff --git a/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java b/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java
index 846c0dd71..41223f27b 100644
--- a/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java
+++ b/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java
@@ -337,7 +337,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
.setReason(Reason.SERVER_STATUS)
.setTargetId(targetId)
.setRegistrarId(registrarId)
- .setCost(Tld.get(existingDomain.getTld()).getServerStatusChangeCost())
+ .setCost(Tld.get(existingDomain.getTld()).getServerStatusChangeBillingCost())
.setEventTime(now)
.setBillingTime(now)
.setDomainHistory(historyEntry)
diff --git a/core/src/main/java/google/registry/model/pricing/StaticPremiumListPricingEngine.java b/core/src/main/java/google/registry/model/pricing/StaticPremiumListPricingEngine.java
index a3e40c12c..ca69c6d03 100644
--- a/core/src/main/java/google/registry/model/pricing/StaticPremiumListPricingEngine.java
+++ b/core/src/main/java/google/registry/model/pricing/StaticPremiumListPricingEngine.java
@@ -42,7 +42,7 @@ public final class StaticPremiumListPricingEngine implements PremiumPricingEngin
tld.getPremiumListName().flatMap(pl -> PremiumListDao.getPremiumPrice(pl, label));
return DomainPrices.create(
premiumPrice.isPresent(),
- premiumPrice.orElse(tld.getStandardCreateCost()),
+ premiumPrice.orElse(tld.getCreateBillingCost()),
premiumPrice.orElse(tld.getStandardRenewCost(priceTime)));
}
}
diff --git a/core/src/main/java/google/registry/model/tld/Tld.java b/core/src/main/java/google/registry/model/tld/Tld.java
index 1d3fcf7bf..4178f0e7e 100644
--- a/core/src/main/java/google/registry/model/tld/Tld.java
+++ b/core/src/main/java/google/registry/model/tld/Tld.java
@@ -26,6 +26,10 @@ import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import static org.joda.money.CurrencyUnit.USD;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.annotations.VisibleForTesting;
@@ -48,6 +52,15 @@ import google.registry.model.domain.fee.BaseFee.FeeType;
import google.registry.model.domain.fee.Fee;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.TokenType;
+import google.registry.model.tld.TldYamlUtils.CreateAutoTimestampDeserializer;
+import google.registry.model.tld.TldYamlUtils.CurrencyDeserializer;
+import google.registry.model.tld.TldYamlUtils.CurrencySerializer;
+import google.registry.model.tld.TldYamlUtils.OptionalDurationSerializer;
+import google.registry.model.tld.TldYamlUtils.OptionalStringSerializer;
+import google.registry.model.tld.TldYamlUtils.TimedTransitionPropertyMoneyDeserializer;
+import google.registry.model.tld.TldYamlUtils.TimedTransitionPropertyTldStateDeserializer;
+import google.registry.model.tld.TldYamlUtils.TokenVKeyListDeserializer;
+import google.registry.model.tld.TldYamlUtils.TokenVKeyListSerializer;
import google.registry.model.tld.label.PremiumList;
import google.registry.model.tld.label.ReservedList;
import google.registry.persistence.VKey;
@@ -281,6 +294,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
*
*
When this field is null, the "dnsDefaultATtl" value from the config file will be used.
*/
+ @JsonSerialize(using = OptionalDurationSerializer.class)
Duration dnsAPlusAaaaTtl;
/**
@@ -288,6 +302,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
*
*
When this field is null, the "dnsDefaultNsTtl" value from the config file will be used.
*/
+ @JsonSerialize(using = OptionalDurationSerializer.class)
Duration dnsNsTtl;
/**
@@ -295,6 +310,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
*
*
When this field is null, the "dnsDefaultDsTtl" value from the config file will be used.
*/
+ @JsonSerialize(using = OptionalDurationSerializer.class)
Duration dnsDsTtl;
/**
* The unicode-aware representation of the TLD associated with this {@link Tld}.
@@ -328,11 +344,13 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
/** A property that transitions to different {@link TldState}s at different times. */
@Column(nullable = false)
+ @JsonDeserialize(using = TimedTransitionPropertyTldStateDeserializer.class)
TimedTransitionProperty tldStateTransitions =
TimedTransitionProperty.withInitialValue(DEFAULT_TLD_STATE);
/** An automatically managed creation timestamp. */
@Column(nullable = false)
+ @JsonDeserialize(using = CreateAutoTimestampDeserializer.class)
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/** The set of reserved list names that are applicable to this tld. */
@@ -359,6 +377,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
* the database should be queried for the entity with this name that has the largest revision ID.
*/
@Column(name = "premium_list_name")
+ @JsonSerialize(using = OptionalStringSerializer.class)
String premiumListName;
/** Should RDE upload a nightly escrow deposit for this TLD? */
@@ -408,6 +427,8 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
/** The currency unit for all costs associated with this TLD. */
@Column(nullable = false)
+ @JsonSerialize(using = CurrencySerializer.class)
+ @JsonDeserialize(using = CurrencyDeserializer.class)
CurrencyUnit currency = DEFAULT_CURRENCY;
/** The per-year billing cost for registering a new domain name. */
@@ -454,11 +475,13 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
* renewal to ensure transfers have a cost.
*/
@Column(nullable = false)
+ @JsonDeserialize(using = TimedTransitionPropertyMoneyDeserializer.class)
TimedTransitionProperty renewBillingCostTransitions =
TimedTransitionProperty.withInitialValue(DEFAULT_RENEW_BILLING_COST);
/** A property that tracks the EAP fee schedule (if any) for the TLD. */
@Column(nullable = false)
+ @JsonDeserialize(using = TimedTransitionPropertyMoneyDeserializer.class)
TimedTransitionProperty eapFeeSchedule =
TimedTransitionProperty.withInitialValue(DEFAULT_EAP_BILLING_COST);
@@ -475,6 +498,11 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
/** An allowlist of hosts allowed to be used on domains on this TLD (ignored if empty). */
@Nullable Set allowedFullyQualifiedHostNames;
+ /**
+ * Indicates when the TLD is being modified using locally modified files to override the source
+ * control procedures. This field is ignored in Tld YAML files.
+ */
+ @JsonIgnore
@Column(nullable = false)
boolean breakglassMode = false;
@@ -488,6 +516,8 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
* (ex: add a token to the list or remove a token from the list) should not be allowed without
* resetting the entire list contents.
*/
+ @JsonSerialize(using = TokenVKeyListSerializer.class)
+ @JsonDeserialize(using = TokenVKeyListDeserializer.class)
List> defaultPromoTokens;
/** A set of allowed {@link IdnTableEnum}s for this TLD, or empty if we should use the default. */
@@ -502,6 +532,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
}
/** Retrieve the actual domain name representing the TLD for which this registry operates. */
+ @JsonIgnore
public InternetDomainName getTld() {
return InternetDomainName.from(tldStr);
}
@@ -511,6 +542,11 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
return tldType;
}
+ /** Retrieve whether invoicing is enabled. */
+ public boolean isInvoicingEnabled() {
+ return invoicingEnabled;
+ }
+
/**
* Retrieve the TLD state at the given time. Defaults to {@link TldState#PREDELEGATION}.
*
@@ -588,7 +624,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
* domain create.
*/
@VisibleForTesting
- public Money getStandardCreateCost() {
+ public Money getCreateBillingCost() {
return createBillingCost;
}
@@ -596,7 +632,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
* Returns the add-on cost of a domain restore (the flat tld-wide fee charged in addition to one
* year of renewal for that name).
*/
- public Money getStandardRestoreCost() {
+ public Money getRestoreBillingCost() {
return restoreBillingCost;
}
@@ -610,7 +646,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
}
/** Returns the cost of a server status change (i.e. lock). */
- public Money getServerStatusChangeCost() {
+ public Money getServerStatusChangeBillingCost() {
return serverStatusChangeBillingCost;
}
@@ -648,6 +684,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
}
@VisibleForTesting
+ @JsonProperty("eapFeeSchedule")
public ImmutableSortedMap getEapFeeScheduleAsMap() {
return eapFeeSchedule.toValueMap();
}
@@ -660,7 +697,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
return claimsPeriodEnd;
}
- public String getPremiumPricingEngineClassName() {
+ public String getPricingEngineClassName() {
return pricingEngineClassName;
}
@@ -688,6 +725,11 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
return Optional.ofNullable(dnsDsTtl);
}
+ /** Retrieve the TLD unicode representation. */
+ public String getTldUnicode() {
+ return tldUnicode;
+ }
+
public ImmutableSet getAllowedRegistrantContactIds() {
return nullToEmptyImmutableCopy(allowedRegistrantContactIds);
}
@@ -1037,13 +1079,13 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
// All costs must be in the expected currency.
checkArgumentNotNull(instance.getCurrency(), "Currency must be set");
checkArgument(
- instance.getStandardCreateCost().getCurrencyUnit().equals(instance.currency),
+ instance.getCreateBillingCost().getCurrencyUnit().equals(instance.currency),
"Create cost must be in the tld's currency");
checkArgument(
- instance.getStandardRestoreCost().getCurrencyUnit().equals(instance.currency),
+ instance.getRestoreBillingCost().getCurrencyUnit().equals(instance.currency),
"Restore cost must be in the TLD's currency");
checkArgument(
- instance.getServerStatusChangeCost().getCurrencyUnit().equals(instance.currency),
+ instance.getServerStatusChangeBillingCost().getCurrencyUnit().equals(instance.currency),
"Server status change cost must be in the TLD's currency");
checkArgument(
instance.getRegistryLockOrUnlockBillingCost().getCurrencyUnit().equals(instance.currency),
diff --git a/core/src/main/java/google/registry/model/tld/TldYamlUtils.java b/core/src/main/java/google/registry/model/tld/TldYamlUtils.java
new file mode 100644
index 000000000..d6a969de0
--- /dev/null
+++ b/core/src/main/java/google/registry/model/tld/TldYamlUtils.java
@@ -0,0 +1,308 @@
+// Copyright 2023 The Nomulus Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package google.registry.model.tld;
+
+import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap;
+import static com.google.common.collect.Ordering.natural;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator.Feature;
+import google.registry.model.CreateAutoTimestamp;
+import google.registry.model.common.TimedTransitionProperty;
+import google.registry.model.domain.token.AllocationToken;
+import google.registry.model.tld.Tld.TldState;
+import google.registry.persistence.VKey;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Optional;
+import java.util.SortedMap;
+import org.joda.money.CurrencyUnit;
+import org.joda.money.Money;
+import org.joda.time.DateTime;
+import org.joda.time.Duration;
+
+/** A collection of static utility classes and functions for TLD YAML conversions. */
+public class TldYamlUtils {
+
+ /**
+ * Returns an {@link ObjectMapper} object that can be used to convert a {@link Tld} object to and
+ * from YAML.
+ */
+ public static ObjectMapper getObjectMapper() {
+ SimpleModule module = new SimpleModule();
+ module.addSerializer(Money.class, new MoneySerializer());
+ module.addDeserializer(Money.class, new MoneyDeserializer());
+ ObjectMapper mapper =
+ new ObjectMapper(new YAMLFactory().disable(Feature.WRITE_DOC_START_MARKER))
+ .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
+ .registerModule(module);
+ mapper.findAndRegisterModules();
+ return mapper;
+ }
+
+ /** A custom JSON serializer for {@link Money}. */
+ public static class MoneySerializer extends StdSerializer {
+
+ public MoneySerializer() {
+ this(null);
+ }
+
+ public MoneySerializer(Class t) {
+ super(t);
+ }
+
+ @Override
+ public void serialize(Money value, JsonGenerator gen, SerializerProvider provider)
+ throws IOException {
+ gen.writeStartObject();
+ gen.writeStringField("currency", String.valueOf(value.getCurrencyUnit()));
+ gen.writeNumberField("amount", value.getAmount());
+ gen.writeEndObject();
+ }
+ }
+
+ /** A custom JSON deserializer for {@link Money}. */
+ public static class MoneyDeserializer extends StdDeserializer {
+ public MoneyDeserializer() {
+ this(null);
+ }
+
+ public MoneyDeserializer(Class t) {
+ super(t);
+ }
+
+ static class MoneyJson {
+ public String currency;
+ public BigDecimal amount;
+ }
+
+ @Override
+ public Money deserialize(JsonParser jp, DeserializationContext context) throws IOException {
+ MoneyJson json = jp.readValueAs(MoneyJson.class);
+ CurrencyUnit currencyUnit = CurrencyUnit.of(json.currency);
+ return Money.of(currencyUnit, json.amount);
+ }
+ }
+
+ /** A custom JSON serializer for {@link CurrencyUnit}. */
+ public static class CurrencySerializer extends StdSerializer {
+
+ public CurrencySerializer() {
+ this(null);
+ }
+
+ public CurrencySerializer(Class t) {
+ super(t);
+ }
+
+ @Override
+ public void serialize(CurrencyUnit value, JsonGenerator gen, SerializerProvider provider)
+ throws IOException {
+ gen.writeString(value.getCode());
+ }
+ }
+
+ /** A custom JSON deserializer for {@link CurrencyUnit}. */
+ public static class CurrencyDeserializer extends StdDeserializer {
+ public CurrencyDeserializer() {
+ this(null);
+ }
+
+ public CurrencyDeserializer(Class t) {
+ super(t);
+ }
+
+ @Override
+ public CurrencyUnit deserialize(JsonParser jp, DeserializationContext context)
+ throws IOException {
+ String currencyCode = jp.readValueAs(String.class);
+ return CurrencyUnit.of(currencyCode);
+ }
+ }
+
+ /** A custom JSON serializer for an Optional of a {@link Duration} object. */
+ public static class OptionalDurationSerializer extends StdSerializer> {
+
+ public OptionalDurationSerializer() {
+ this(null);
+ }
+
+ public OptionalDurationSerializer(Class> t) {
+ super(t);
+ }
+
+ @Override
+ public void serialize(Optional value, JsonGenerator gen, SerializerProvider provider)
+ throws IOException {
+ if (value.isPresent()) {
+ gen.writeNumber(value.get().getMillis());
+ } else {
+ gen.writeNull();
+ }
+ }
+ }
+
+ /** A custom JSON serializer for an Optional String. */
+ public static class OptionalStringSerializer extends StdSerializer> {
+
+ public OptionalStringSerializer() {
+ this(null);
+ }
+
+ public OptionalStringSerializer(Class> t) {
+ super(t);
+ }
+
+ @Override
+ public void serialize(Optional value, JsonGenerator gen, SerializerProvider provider)
+ throws IOException {
+ if (value.isPresent()) {
+ gen.writeString(value.get());
+ } else {
+ gen.writeNull();
+ }
+ }
+ }
+
+ /** A custom JSON serializer for a list of {@link AllocationToken} VKeys. */
+ public static class TokenVKeyListSerializer extends StdSerializer>> {
+
+ public TokenVKeyListSerializer() {
+ this(null);
+ }
+
+ public TokenVKeyListSerializer(Class>> t) {
+ super(t);
+ }
+
+ @Override
+ public void serialize(
+ List> list, JsonGenerator gen, SerializerProvider provider)
+ throws IOException {
+ gen.writeStartArray();
+ for (VKey vkey : list) {
+ gen.writeString(vkey.getKey().toString());
+ }
+ gen.writeEndArray();
+ }
+ }
+
+ /** A custom JSON deserializer for a list of {@link AllocationToken} VKeys. */
+ public static class TokenVKeyListDeserializer
+ extends StdDeserializer>> {
+
+ public TokenVKeyListDeserializer() {
+ this(null);
+ }
+
+ public TokenVKeyListDeserializer(Class> t) {
+ super(t);
+ }
+
+ @Override
+ public List> deserialize(JsonParser jp, DeserializationContext context)
+ throws IOException {
+ List> tokens = new ArrayList<>();
+ String[] keyStrings = jp.readValueAs(String[].class);
+ for (String token : keyStrings) {
+ tokens.add(VKey.create(AllocationToken.class, token));
+ }
+ return tokens;
+ }
+ }
+
+ /** A custom JSON deserializer for a {@link TimedTransitionProperty} of {@link TldState}. */
+ public static class TimedTransitionPropertyTldStateDeserializer
+ extends StdDeserializer> {
+
+ public TimedTransitionPropertyTldStateDeserializer() {
+ this(null);
+ }
+
+ public TimedTransitionPropertyTldStateDeserializer(Class> t) {
+ super(t);
+ }
+
+ @Override
+ public TimedTransitionProperty deserialize(
+ JsonParser jp, DeserializationContext context) throws IOException {
+ SortedMap valueMap = jp.readValueAs(SortedMap.class);
+ return TimedTransitionProperty.fromValueMap(
+ valueMap.keySet().stream()
+ .collect(
+ toImmutableSortedMap(
+ natural(), DateTime::parse, key -> TldState.valueOf(valueMap.get(key)))));
+ }
+ }
+
+ /** A custom JSON deserializer for a {@link TimedTransitionProperty} of {@link Money}. */
+ public static class TimedTransitionPropertyMoneyDeserializer
+ extends StdDeserializer> {
+
+ public TimedTransitionPropertyMoneyDeserializer() {
+ this(null);
+ }
+
+ public TimedTransitionPropertyMoneyDeserializer(Class> t) {
+ super(t);
+ }
+
+ @Override
+ public TimedTransitionProperty deserialize(JsonParser jp, DeserializationContext context)
+ throws IOException {
+ SortedMap valueMap = jp.readValueAs(SortedMap.class);
+ return TimedTransitionProperty.fromValueMap(
+ valueMap.keySet().stream()
+ .collect(
+ toImmutableSortedMap(
+ natural(),
+ DateTime::parse,
+ key ->
+ Money.of(
+ CurrencyUnit.of(valueMap.get(key).get("currency").toString()),
+ (double) valueMap.get(key).get("amount")))));
+ }
+ }
+
+ /** A custom JSON deserializer for a {@link CreateAutoTimestamp}. */
+ public static class CreateAutoTimestampDeserializer extends StdDeserializer {
+
+ public CreateAutoTimestampDeserializer() {
+ this(null);
+ }
+
+ public CreateAutoTimestampDeserializer(Class t) {
+ super(t);
+ }
+
+ @Override
+ public CreateAutoTimestamp deserialize(JsonParser jp, DeserializationContext context)
+ throws IOException {
+ DateTime creationTime = jp.readValueAs(DateTime.class);
+ return CreateAutoTimestamp.create(creationTime);
+ }
+ }
+}
diff --git a/core/src/main/java/google/registry/pricing/PricingEngineProxy.java b/core/src/main/java/google/registry/pricing/PricingEngineProxy.java
index b22a42749..f50a82e5a 100644
--- a/core/src/main/java/google/registry/pricing/PricingEngineProxy.java
+++ b/core/src/main/java/google/registry/pricing/PricingEngineProxy.java
@@ -58,7 +58,7 @@ public final class PricingEngineProxy {
*/
public static DomainPrices getPricesForDomainName(String domainName, DateTime priceTime) {
String tld = getTldFromDomainName(domainName);
- String clazz = Tld.get(tld).getPremiumPricingEngineClassName();
+ String clazz = Tld.get(tld).getPricingEngineClassName();
PremiumPricingEngine engine = premiumPricingEngines.get(clazz);
checkState(engine != null, "Could not load pricing engine %s for TLD %s", clazz, tld);
return engine.getDomainPrices(domainName, priceTime);
diff --git a/core/src/test/java/google/registry/beam/common/DatabaseSnapshotTest.java b/core/src/test/java/google/registry/beam/common/DatabaseSnapshotTest.java
index dca7108a2..c9eaa3ec8 100644
--- a/core/src/test/java/google/registry/beam/common/DatabaseSnapshotTest.java
+++ b/core/src/test/java/google/registry/beam/common/DatabaseSnapshotTest.java
@@ -127,7 +127,7 @@ public class DatabaseSnapshotTest {
Tld updated =
registry
.asBuilder()
- .setCreateBillingCost(registry.getStandardCreateCost().plus(1))
+ .setCreateBillingCost(registry.getCreateBillingCost().plus(1))
.build();
tm().transact(() -> tm().put(updated));
@@ -152,7 +152,7 @@ public class DatabaseSnapshotTest {
Tld updated =
registry
.asBuilder()
- .setCreateBillingCost(registry.getStandardCreateCost().plus(1))
+ .setCreateBillingCost(registry.getCreateBillingCost().plus(1))
.build();
tm().transact(() -> tm().put(updated));
diff --git a/core/src/test/java/google/registry/model/tld/TldTest.java b/core/src/test/java/google/registry/model/tld/TldTest.java
index e29645f03..cd1aa33a7 100644
--- a/core/src/test/java/google/registry/model/tld/TldTest.java
+++ b/core/src/test/java/google/registry/model/tld/TldTest.java
@@ -17,18 +17,22 @@ package google.registry.model.tld;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
+import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO;
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
import static google.registry.model.tld.Tld.TldState.PREDELEGATION;
import static google.registry.model.tld.Tld.TldState.QUIET_PERIOD;
import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE;
+import static google.registry.model.tld.TldYamlUtils.getObjectMapper;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.newTld;
import static google.registry.testing.DatabaseHelper.persistPremiumList;
import static google.registry.testing.DatabaseHelper.persistReservedList;
import static google.registry.testing.DatabaseHelper.persistResource;
+import static google.registry.testing.TestDataHelper.filePath;
+import static google.registry.testing.TestDataHelper.loadFile;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static java.math.RoundingMode.UNNECESSARY;
@@ -36,6 +40,7 @@ import static org.joda.money.CurrencyUnit.EUR;
import static org.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
@@ -48,11 +53,14 @@ import google.registry.model.tld.label.PremiumList;
import google.registry.model.tld.label.PremiumListDao;
import google.registry.model.tld.label.ReservedList;
import google.registry.persistence.VKey;
+import google.registry.tldconfig.idn.IdnTableEnum;
import google.registry.util.SerializeUtils;
+import java.io.File;
import java.math.BigDecimal;
import java.util.Optional;
import org.joda.money.Money;
import org.joda.time.DateTime;
+import org.joda.time.Duration;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -94,6 +102,106 @@ public final class TldTest extends EntityTestCase {
assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
}
+ @Test
+ void testTldToYaml() throws Exception {
+ fakeClock.setTo(START_OF_TIME);
+ AllocationToken defaultToken =
+ persistResource(
+ new AllocationToken.Builder()
+ .setToken("bbbbb")
+ .setTokenType(DEFAULT_PROMO)
+ .setAllowedTlds(ImmutableSet.of("tld"))
+ .build());
+ Tld existingTld =
+ createTld("tld")
+ .asBuilder()
+ .setDnsAPlusAaaaTtl(Duration.standardHours(1))
+ .setDnsWriters(ImmutableSet.of("baz", "bang"))
+ .setEapFeeSchedule(
+ ImmutableSortedMap.of(
+ START_OF_TIME,
+ Money.of(USD, 0),
+ DateTime.parse("2000-06-01T00:00:00Z"),
+ Money.of(USD, 100),
+ DateTime.parse("2000-06-02T00:00:00Z"),
+ Money.of(USD, 0)))
+ .setAllowedFullyQualifiedHostNames(ImmutableSet.of("foo"))
+ .setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
+ .setIdnTables(ImmutableSet.of(IdnTableEnum.JA, IdnTableEnum.EXTENDED_LATIN))
+ .build();
+
+ ObjectMapper mapper = getObjectMapper();
+ String yaml = mapper.writeValueAsString(existingTld);
+ assertThat(yaml).isEqualTo(loadFile(getClass(), "tld.yaml"));
+ }
+
+ @Test
+ void testYamlToTld() throws Exception {
+ fakeClock.setTo(START_OF_TIME);
+ AllocationToken defaultToken =
+ persistResource(
+ new AllocationToken.Builder()
+ .setToken("bbbbb")
+ .setTokenType(DEFAULT_PROMO)
+ .setAllowedTlds(ImmutableSet.of("tld"))
+ .build());
+ Tld existingTld =
+ createTld("tld")
+ .asBuilder()
+ .setDnsAPlusAaaaTtl(Duration.standardHours(1))
+ .setDnsWriters(ImmutableSet.of("baz", "bang"))
+ .setEapFeeSchedule(
+ ImmutableSortedMap.of(
+ START_OF_TIME,
+ Money.of(USD, 0),
+ DateTime.parse("2000-06-01T00:00:00Z"),
+ Money.of(USD, 100),
+ DateTime.parse("2000-06-02T00:00:00Z"),
+ Money.of(USD, 0)))
+ .setAllowedFullyQualifiedHostNames(ImmutableSet.of("foo"))
+ .setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
+ .setIdnTables(ImmutableSet.of(IdnTableEnum.JA, IdnTableEnum.EXTENDED_LATIN))
+ .build();
+
+ ObjectMapper mapper = getObjectMapper();
+ Tld constructedTld = mapper.readValue(new File(filePath(getClass(), "tld.yaml")), Tld.class);
+ compareTlds(existingTld, constructedTld);
+ }
+
+ @Test
+ void testSuccess_tldYamlRoundtrip() throws Exception {
+ Tld testTld = createTld("test");
+ ObjectMapper mapper = getObjectMapper();
+ String yaml = mapper.writeValueAsString(testTld);
+ Tld constructedTld = mapper.readValue(yaml, Tld.class);
+ compareTlds(testTld, constructedTld);
+ }
+
+ // On YAML serialization/deserialization some null values may be changed to empty collections
+ void compareTlds(Tld existingTld, Tld constructedTld) {
+ assertAboutImmutableObjects()
+ .that(constructedTld)
+ .isEqualExceptFields(
+ existingTld,
+ "dnsWriters",
+ "idnTables",
+ "reservedListNames",
+ "allowedRegistrantContactIds",
+ "allowedFullyQualifiedHostNames",
+ "defaultPromoTokens");
+ assertThat(constructedTld.getDnsWriters())
+ .containsExactlyElementsIn(existingTld.getDnsWriters());
+ assertThat(constructedTld.getIdnTables()).containsExactlyElementsIn(existingTld.getIdnTables());
+ assertThat(constructedTld.getReservedListNames())
+ .containsExactlyElementsIn(existingTld.getReservedListNames());
+ assertThat(constructedTld.getAllowedRegistrantContactIds())
+ .containsExactlyElementsIn(existingTld.getAllowedRegistrantContactIds());
+ assertThat(constructedTld.getAllowedFullyQualifiedHostNames())
+ .containsExactlyElementsIn(existingTld.getAllowedFullyQualifiedHostNames());
+ assertThat(constructedTld.getDefaultPromoTokens())
+ .containsExactlyElementsIn(existingTld.getDefaultPromoTokens());
+ }
+
@Test
void testFailure_registryNotFound() {
createTld("foo");
@@ -111,17 +219,17 @@ public final class TldTest extends EntityTestCase {
@Test
void testSettingCreateBillingCost() {
Tld registry = Tld.get("tld").asBuilder().setCreateBillingCost(Money.of(USD, 42)).build();
- assertThat(registry.getStandardCreateCost()).isEqualTo(Money.of(USD, 42));
+ assertThat(registry.getCreateBillingCost()).isEqualTo(Money.of(USD, 42));
// The default value of 17 is set in createTld().
- assertThat(registry.getStandardRestoreCost()).isEqualTo(Money.of(USD, 17));
+ assertThat(registry.getRestoreBillingCost()).isEqualTo(Money.of(USD, 17));
}
@Test
void testSettingRestoreBillingCost() {
Tld registry = Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, 42)).build();
// The default value of 13 is set in createTld().
- assertThat(registry.getStandardCreateCost()).isEqualTo(Money.of(USD, 13));
- assertThat(registry.getStandardRestoreCost()).isEqualTo(Money.of(USD, 42));
+ assertThat(registry.getCreateBillingCost()).isEqualTo(Money.of(USD, 13));
+ assertThat(registry.getRestoreBillingCost()).isEqualTo(Money.of(USD, 42));
}
@Test
@@ -251,7 +359,7 @@ public final class TldTest extends EntityTestCase {
void testSettingServerStatusChangeBillingCost() {
Tld registry =
Tld.get("tld").asBuilder().setServerStatusChangeBillingCost(Money.of(USD, 42)).build();
- assertThat(registry.getServerStatusChangeCost()).isEqualTo(Money.of(USD, 42));
+ assertThat(registry.getServerStatusChangeBillingCost()).isEqualTo(Money.of(USD, 42));
}
@Test
diff --git a/core/src/test/java/google/registry/tools/CreateTldCommandTest.java b/core/src/test/java/google/registry/tools/CreateTldCommandTest.java
index f888d122f..ccfc1af19 100644
--- a/core/src/test/java/google/registry/tools/CreateTldCommandTest.java
+++ b/core/src/test/java/google/registry/tools/CreateTldCommandTest.java
@@ -225,7 +225,7 @@ class CreateTldCommandTest extends CommandTestCase {
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
- assertThat(Tld.get("xn--q9jyb4c").getStandardCreateCost()).isEqualTo(Money.of(USD, 42.42));
+ assertThat(Tld.get("xn--q9jyb4c").getCreateBillingCost()).isEqualTo(Money.of(USD, 42.42));
}
@Test
@@ -235,7 +235,7 @@ class CreateTldCommandTest extends CommandTestCase {
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
- assertThat(Tld.get("xn--q9jyb4c").getStandardRestoreCost()).isEqualTo(Money.of(USD, 42.42));
+ assertThat(Tld.get("xn--q9jyb4c").getRestoreBillingCost()).isEqualTo(Money.of(USD, 42.42));
}
@Test
@@ -245,7 +245,8 @@ class CreateTldCommandTest extends CommandTestCase {
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
- assertThat(Tld.get("xn--q9jyb4c").getServerStatusChangeCost()).isEqualTo(Money.of(USD, 42.42));
+ assertThat(Tld.get("xn--q9jyb4c").getServerStatusChangeBillingCost())
+ .isEqualTo(Money.of(USD, 42.42));
}
@Test
@@ -271,8 +272,8 @@ class CreateTldCommandTest extends CommandTestCase {
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
Tld registry = Tld.get("xn--q9jyb4c");
- assertThat(registry.getStandardCreateCost()).isEqualTo(Money.ofMajor(JPY, 12345));
- assertThat(registry.getStandardRestoreCost()).isEqualTo(Money.ofMajor(JPY, 67890));
+ assertThat(registry.getCreateBillingCost()).isEqualTo(Money.ofMajor(JPY, 12345));
+ assertThat(registry.getRestoreBillingCost()).isEqualTo(Money.ofMajor(JPY, 67890));
assertThat(registry.getStandardRenewCost(START_OF_TIME)).isEqualTo(Money.ofMajor(JPY, 101112));
}
diff --git a/core/src/test/java/google/registry/tools/UpdateTldCommandTest.java b/core/src/test/java/google/registry/tools/UpdateTldCommandTest.java
index e1b512261..30940d826 100644
--- a/core/src/test/java/google/registry/tools/UpdateTldCommandTest.java
+++ b/core/src/test/java/google/registry/tools/UpdateTldCommandTest.java
@@ -299,13 +299,13 @@ class UpdateTldCommandTest extends CommandTestCase {
@Test
void testSuccess_createBillingCostFlag() throws Exception {
runCommandForced("--create_billing_cost=\"USD 42.42\"", "xn--q9jyb4c");
- assertThat(Tld.get("xn--q9jyb4c").getStandardCreateCost()).isEqualTo(Money.of(USD, 42.42));
+ assertThat(Tld.get("xn--q9jyb4c").getCreateBillingCost()).isEqualTo(Money.of(USD, 42.42));
}
@Test
void testSuccess_restoreBillingCostFlag() throws Exception {
runCommandForced("--restore_billing_cost=\"USD 42.42\"", "xn--q9jyb4c");
- assertThat(Tld.get("xn--q9jyb4c").getStandardRestoreCost()).isEqualTo(Money.of(USD, 42.42));
+ assertThat(Tld.get("xn--q9jyb4c").getRestoreBillingCost()).isEqualTo(Money.of(USD, 42.42));
}
@Test
@@ -330,10 +330,10 @@ class UpdateTldCommandTest extends CommandTestCase {
"--registry_lock_or_unlock_cost=\"JPY 9001\"",
"xn--q9jyb4c");
Tld registry = Tld.get("xn--q9jyb4c");
- assertThat(registry.getStandardCreateCost()).isEqualTo(Money.ofMajor(JPY, 12345));
- assertThat(registry.getStandardRestoreCost()).isEqualTo(Money.ofMajor(JPY, 67890));
+ assertThat(registry.getCreateBillingCost()).isEqualTo(Money.ofMajor(JPY, 12345));
+ assertThat(registry.getRestoreBillingCost()).isEqualTo(Money.ofMajor(JPY, 67890));
assertThat(registry.getStandardRenewCost(START_OF_TIME)).isEqualTo(Money.ofMajor(JPY, 101112));
- assertThat(registry.getServerStatusChangeCost()).isEqualTo(Money.ofMajor(JPY, 97865));
+ assertThat(registry.getServerStatusChangeBillingCost()).isEqualTo(Money.ofMajor(JPY, 97865));
assertThat(registry.getRegistryLockOrUnlockBillingCost()).isEqualTo(Money.ofMajor(JPY, 9001));
}
diff --git a/core/src/test/resources/google/registry/model/tld/tld.yaml b/core/src/test/resources/google/registry/model/tld/tld.yaml
new file mode 100644
index 000000000..23419904d
--- /dev/null
+++ b/core/src/test/resources/google/registry/model/tld/tld.yaml
@@ -0,0 +1,66 @@
+tldStr: "tld"
+roidSuffix: "TLD"
+pricingEngineClassName: "google.registry.model.pricing.StaticPremiumListPricingEngine"
+dnsWriters:
+- "baz"
+- "bang"
+numDnsPublishLocks: 1
+dnsAPlusAaaaTtl: 3600000
+dnsNsTtl: null
+dnsDsTtl: null
+tldUnicode: "tld"
+driveFolderId: null
+tldType: "REAL"
+invoicingEnabled: false
+tldStateTransitions:
+ "1970-01-01T00:00:00.000Z": "GENERAL_AVAILABILITY"
+creationTime: "1970-01-01T00:00:00.000Z"
+reservedListNames: []
+premiumListName: "tld"
+escrowEnabled: false
+dnsPaused: false
+addGracePeriodLength: 432000000
+anchorTenantAddGracePeriodLength: 2592000000
+autoRenewGracePeriodLength: 3888000000
+redemptionGracePeriodLength: 2592000000
+renewGracePeriodLength: 432000000
+transferGracePeriodLength: 432000000
+automaticTransferLength: 432000000
+pendingDeleteLength: 432000000
+currency: "USD"
+createBillingCost:
+ currency: "USD"
+ amount: 13.00
+restoreBillingCost:
+ currency: "USD"
+ amount: 17.00
+serverStatusChangeBillingCost:
+ currency: "USD"
+ amount: 19.00
+registryLockOrUnlockBillingCost:
+ currency: "USD"
+ amount: 0.00
+renewBillingCostTransitions:
+ "1970-01-01T00:00:00.000Z":
+ currency: "USD"
+ amount: 11.00
+lordnUsername: null
+claimsPeriodEnd: "294247-01-10T04:00:54.775Z"
+allowedRegistrantContactIds: []
+allowedFullyQualifiedHostNames:
+- "foo"
+defaultPromoTokens:
+- "bbbbb"
+idnTables:
+- "JA"
+- "EXTENDED_LATIN"
+eapFeeSchedule:
+ "1970-01-01T00:00:00.000Z":
+ currency: "USD"
+ amount: 0.00
+ "2000-06-01T00:00:00.000Z":
+ currency: "USD"
+ amount: 100.00
+ "2000-06-02T00:00:00.000Z":
+ currency: "USD"
+ amount: 0.00