diff --git a/core/src/main/java/google/registry/export/ExportDomainListsAction.java b/core/src/main/java/google/registry/export/ExportDomainListsAction.java index 88b4712c9..02223c5ed 100644 --- a/core/src/main/java/google/registry/export/ExportDomainListsAction.java +++ b/core/src/main/java/google/registry/export/ExportDomainListsAction.java @@ -15,6 +15,7 @@ package google.registry.export; import static com.google.common.base.Verify.verifyNotNull; +import static google.registry.model.common.FeatureFlag.FeatureName.INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS; import static google.registry.model.tld.Tlds.getTldsOfType; import static google.registry.persistence.PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ; import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm; @@ -75,7 +76,8 @@ public class ExportDomainListsAction implements Runnable { ON d.repo_id = gp.domain_repo_id WHERE d.tld = :tld AND d.deletion_time > CAST(:now AS timestamptz) - ORDER BY d.domain_name"""; + ORDER BY d.domain_name + """; // This may be a CSV, but it is uses a .txt file extension for back-compatibility static final String REGISTERED_DOMAINS_FILENAME_FORMAT = "registered_domains_%s.txt"; @@ -93,10 +95,7 @@ public class ExportDomainListsAction implements Runnable { logger.atInfo().log("Exporting domain lists for TLDs %s.", realTlds); boolean includeDeletionTimes = - tm().transact( - () -> - FeatureFlag.isActiveNowOrElse( - FeatureFlag.FeatureName.INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS, false)); + tm().transact(() -> FeatureFlag.isActiveNow(INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS)); realTlds.forEach( tld -> { List domainsList = diff --git a/core/src/main/java/google/registry/flows/contact/ContactCreateFlow.java b/core/src/main/java/google/registry/flows/contact/ContactCreateFlow.java index a12eca9cc..364ef2f74 100644 --- a/core/src/main/java/google/registry/flows/contact/ContactCreateFlow.java +++ b/core/src/main/java/google/registry/flows/contact/ContactCreateFlow.java @@ -19,6 +19,7 @@ import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist import static google.registry.flows.contact.ContactFlowUtils.validateAsciiPostalInfo; import static google.registry.flows.contact.ContactFlowUtils.validateContactAgainstPolicy; import static google.registry.model.EppResourceUtils.createRepoId; +import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import com.google.common.collect.ImmutableSet; @@ -29,8 +30,10 @@ import google.registry.flows.FlowModule.RegistrarId; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.MutatingFlow; import google.registry.flows.annotations.ReportingSpec; +import google.registry.flows.exceptions.ContactsProhibitedException; import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientException; import google.registry.flows.exceptions.ResourceCreateContentionException; +import google.registry.model.common.FeatureFlag; import google.registry.model.contact.Contact; import google.registry.model.contact.ContactCommand.Create; import google.registry.model.contact.ContactHistory; @@ -47,6 +50,7 @@ import org.joda.time.DateTime; * An EPP flow that creates a new contact. * * @error {@link google.registry.flows.FlowUtils.NotLoggedInException} + * @error {@link ContactsProhibitedException} * @error {@link ResourceAlreadyExistsForThisClientException} * @error {@link ResourceCreateContentionException} * @error {@link ContactFlowUtils.BadInternationalizedPostalInfoException} @@ -69,6 +73,9 @@ public final class ContactCreateFlow implements MutatingFlow { extensionManager.register(MetadataExtension.class); validateRegistrarIsLoggedIn(registrarId); extensionManager.validate(); + if (FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)) { + throw new ContactsProhibitedException(); + } Create command = (Create) resourceCommand; DateTime now = tm().getTransactionTime(); verifyResourceDoesNotExist(Contact.class, targetId, now, registrarId); diff --git a/core/src/main/java/google/registry/flows/contact/ContactUpdateFlow.java b/core/src/main/java/google/registry/flows/contact/ContactUpdateFlow.java index 20050d462..afc3c2070 100644 --- a/core/src/main/java/google/registry/flows/contact/ContactUpdateFlow.java +++ b/core/src/main/java/google/registry/flows/contact/ContactUpdateFlow.java @@ -24,6 +24,7 @@ import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo; import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; import static google.registry.flows.contact.ContactFlowUtils.validateAsciiPostalInfo; import static google.registry.flows.contact.ContactFlowUtils.validateContactAgainstPolicy; +import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED; import static google.registry.model.reporting.HistoryEntry.Type.CONTACT_UPDATE; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; @@ -35,7 +36,9 @@ import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.MutatingFlow; import google.registry.flows.annotations.ReportingSpec; +import google.registry.flows.exceptions.ContactsProhibitedException; import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException; +import google.registry.model.common.FeatureFlag; import google.registry.model.contact.Contact; import google.registry.model.contact.ContactCommand.Update; import google.registry.model.contact.ContactCommand.Update.Change; @@ -55,6 +58,7 @@ import org.joda.time.DateTime; /** * An EPP flow that updates a contact. * + * @error {@link ContactsProhibitedException} * @error {@link google.registry.flows.FlowUtils.NotLoggedInException} * @error {@link google.registry.flows.ResourceFlowUtils.AddRemoveSameValueException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} @@ -92,6 +96,9 @@ public final class ContactUpdateFlow implements MutatingFlow { extensionManager.register(MetadataExtension.class); validateRegistrarIsLoggedIn(registrarId); extensionManager.validate(); + if (FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)) { + throw new ContactsProhibitedException(); + } Update command = (Update) resourceCommand; DateTime now = tm().getTransactionTime(); Contact existingContact = loadAndVerifyExistence(Contact.class, targetId, now); diff --git a/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java b/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java index 088501028..4901ea961 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java @@ -72,7 +72,9 @@ import google.registry.flows.custom.DomainCreateFlowCustomLogic; import google.registry.flows.custom.DomainCreateFlowCustomLogic.BeforeResponseParameters; import google.registry.flows.custom.DomainCreateFlowCustomLogic.BeforeResponseReturnData; import google.registry.flows.custom.EntityChanges; +import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException; import google.registry.flows.domain.token.AllocationTokenFlowUtils; +import google.registry.flows.exceptions.ContactsProhibitedException; import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientException; import google.registry.flows.exceptions.ResourceCreateContentionException; import google.registry.model.ImmutableObject; @@ -147,6 +149,7 @@ import org.joda.time.Duration; * @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException} * @error {@link DomainCreateFlow.NoTrademarkedRegistrationsBeforeSunriseException} * @error {@link BulkDomainRegisteredForTooManyYearsException} + * @error {@link ContactsProhibitedException} * @error {@link DomainCreateFlow.SignedMarksOnlyDuringSunriseException} * @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException} * @error {@link DomainFlowTmchUtils.FoundMarkNotYetValidException} @@ -194,6 +197,7 @@ import org.joda.time.Duration; * @error {@link DomainFlowUtils.NameserversNotSpecifiedForTldWithNameserverAllowListException} * @error {@link DomainFlowUtils.PremiumNameBlockedException} * @error {@link DomainFlowUtils.RegistrantNotAllowedException} + * @error {@link RegistrantProhibitedException} * @error {@link DomainFlowUtils.RegistrarMustBeActiveForThisOperationException} * @error {@link DomainFlowUtils.TldDoesNotExistException} * @error {@link DomainFlowUtils.TooManyDsRecordsException} @@ -244,7 +248,7 @@ public final class DomainCreateFlow implements MutatingFlow { verifyResourceDoesNotExist(Domain.class, targetId, now, registrarId); // Validate that this is actually a legal domain name on a TLD that the registrar has access to. InternetDomainName domainName = validateDomainName(command.getDomainName()); - String domainLabel = domainName.parts().get(0); + String domainLabel = domainName.parts().getFirst(); Tld tld = Tld.get(domainName.parent().toString()); validateCreateCommandContactsAndNameservers(command, tld, domainName); TldState tldState = tld.getTldState(now); diff --git a/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java b/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java index 9011dd823..0740dda5e 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java +++ b/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java @@ -25,7 +25,7 @@ import static com.google.common.collect.Sets.intersection; import static com.google.common.collect.Sets.union; import static google.registry.bsa.persistence.BsaLabelUtils.isLabelBlocked; import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL; -import static google.registry.model.common.FeatureFlag.isActiveNow; +import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED; import static google.registry.model.domain.Domain.MAX_REGISTRATION_YEARS; import static google.registry.model.domain.token.AllocationToken.TokenType.REGISTER_BSA; import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY; @@ -75,11 +75,13 @@ import google.registry.flows.EppException.ParameterValueSyntaxErrorException; import google.registry.flows.EppException.RequiredParameterMissingException; import google.registry.flows.EppException.StatusProhibitsOperationException; import google.registry.flows.EppException.UnimplementedOptionException; +import google.registry.flows.exceptions.ContactsProhibitedException; import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException; import google.registry.model.EppResource; import google.registry.model.billing.BillingBase.Flag; import google.registry.model.billing.BillingBase.Reason; import google.registry.model.billing.BillingRecurrence; +import google.registry.model.common.FeatureFlag; import google.registry.model.contact.Contact; import google.registry.model.domain.DesignatedContact; import google.registry.model.domain.DesignatedContact.Type; @@ -478,27 +480,37 @@ public class DomainFlowUtils { } } - static void validateRequiredContactsPresentIfRequiredForDataset( + /** + * Enforces the presence/absence of contact data depending on the minimum data set migration + * schedule. + */ + static void validateContactDataPresence( Optional> registrant, Set contacts) - throws RequiredParameterMissingException { - // TODO(b/353347632): Change this flag check to a registry config check. - if (isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)) { - // Contacts are not required once we have begun the migration to the minimum dataset - return; - } - if (registrant.isEmpty()) { - throw new MissingRegistrantException(); - } + throws RequiredParameterMissingException, ParameterValuePolicyErrorException { + // TODO(b/353347632): Change these flag checks to a registry config check once minimum data set + // migration is completed. + if (FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)) { + if (registrant.isPresent()) { + throw new RegistrantProhibitedException(); + } + if (!contacts.isEmpty()) { + throw new ContactsProhibitedException(); + } + } else if (!FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)) { + if (registrant.isEmpty()) { + throw new MissingRegistrantException(); + } - Set roles = new HashSet<>(); - for (DesignatedContact contact : contacts) { - roles.add(contact.getType()); - } - if (!roles.contains(Type.ADMIN)) { - throw new MissingAdminContactException(); - } - if (!roles.contains(Type.TECH)) { - throw new MissingTechnicalContactException(); + Set roles = new HashSet<>(); + for (DesignatedContact contact : contacts) { + roles.add(contact.getType()); + } + if (!roles.contains(Type.ADMIN)) { + throw new MissingAdminContactException(); + } + if (!roles.contains(Type.TECH)) { + throw new MissingTechnicalContactException(); + } } } @@ -1042,8 +1054,7 @@ public class DomainFlowUtils { String tldStr = tld.getTldStr(); validateRegistrantAllowedOnTld(tldStr, command.getRegistrantContactId()); validateNoDuplicateContacts(command.getContacts()); - validateRequiredContactsPresentIfRequiredForDataset( - command.getRegistrant(), command.getContacts()); + validateContactDataPresence(command.getRegistrant(), command.getContacts()); ImmutableSet hostNames = command.getNameserverHostNames(); validateNameserversCountForTld(tldStr, domainName, hostNames.size()); validateNameserversAllowedOnTld(tldStr, hostNames); @@ -1367,6 +1378,13 @@ public class DomainFlowUtils { } } + /** Having a registrant is prohibited by registry policy. */ + static class RegistrantProhibitedException extends ParameterValuePolicyErrorException { + public RegistrantProhibitedException() { + super("Having a registrant is prohibited by registry policy"); + } + } + /** Admin contact is required. */ static class MissingAdminContactException extends RequiredParameterMissingException { public MissingAdminContactException() { 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 a3363ef86..c7b3600a4 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java @@ -30,6 +30,7 @@ import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld; import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences; import static google.registry.flows.domain.DomainFlowUtils.updateDsData; +import static google.registry.flows.domain.DomainFlowUtils.validateContactDataPresence; import static google.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes; import static google.registry.flows.domain.DomainFlowUtils.validateDsData; import static google.registry.flows.domain.DomainFlowUtils.validateFeesAckedIfPresent; @@ -37,11 +38,10 @@ import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAl import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCountForTld; import static google.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts; import static google.registry.flows.domain.DomainFlowUtils.validateRegistrantAllowedOnTld; -import static google.registry.flows.domain.DomainFlowUtils.validateRequiredContactsPresentIfRequiredForDataset; import static google.registry.flows.domain.DomainFlowUtils.verifyClientUpdateNotProhibited; import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete; import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL; -import static google.registry.model.common.FeatureFlag.isActiveNow; +import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED; import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_UPDATE; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; @@ -64,9 +64,11 @@ import google.registry.flows.custom.DomainUpdateFlowCustomLogic.BeforeSaveParame import google.registry.flows.custom.EntityChanges; import google.registry.flows.domain.DomainFlowUtils.MissingRegistrantException; import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedForTldWithNameserverAllowListException; +import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException; import google.registry.model.ImmutableObject; import google.registry.model.billing.BillingBase.Reason; import google.registry.model.billing.BillingEvent; +import google.registry.model.common.FeatureFlag; import google.registry.model.contact.Contact; import google.registry.model.domain.DesignatedContact; import google.registry.model.domain.Domain; @@ -130,6 +132,7 @@ import org.joda.time.DateTime; * @error {@link NameserversNotSpecifiedForTldWithNameserverAllowListException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException} * @error {@link DomainFlowUtils.RegistrantNotAllowedException} + * @error {@link RegistrantProhibitedException} * @error {@link DomainFlowUtils.SecDnsAllUsageException} * @error {@link DomainFlowUtils.TooManyDsRecordsException} * @error {@link DomainFlowUtils.TooManyNameserversException} @@ -304,11 +307,12 @@ public final class DomainUpdateFlow implements MutatingFlow { private Optional> determineUpdatedRegistrant(Change change, Domain domain) throws EppException { - // During phase 1 of minimum dataset transition, allow registrant to be removed + // During or after the minimum dataset transition, allow registrant to be removed. if (change.getRegistrantContactId().isPresent() && change.getRegistrantContactId().get().isEmpty()) { // TODO(b/353347632): Change this flag check to a registry config check. - if (isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)) { + if (FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL) + || FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)) { return Optional.empty(); } else { throw new MissingRegistrantException(); @@ -325,8 +329,7 @@ public final class DomainUpdateFlow implements MutatingFlow { * cause Domain update failure. */ private static void validateNewState(Domain newDomain) throws EppException { - validateRequiredContactsPresentIfRequiredForDataset( - newDomain.getRegistrant(), newDomain.getContacts()); + validateContactDataPresence(newDomain.getRegistrant(), newDomain.getContacts()); validateDsData(newDomain.getDsData()); validateNameserversCountForTld( newDomain.getTld(), diff --git a/core/src/main/java/google/registry/flows/exceptions/ContactsProhibitedException.java b/core/src/main/java/google/registry/flows/exceptions/ContactsProhibitedException.java new file mode 100644 index 000000000..2132e05f1 --- /dev/null +++ b/core/src/main/java/google/registry/flows/exceptions/ContactsProhibitedException.java @@ -0,0 +1,24 @@ +// Copyright 2025 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.flows.exceptions; + +import google.registry.flows.EppException.ParameterValuePolicyErrorException; + +/** Having contacts is prohibited by registry policy */ +public class ContactsProhibitedException extends ParameterValuePolicyErrorException { + public ContactsProhibitedException() { + super("Having contacts is prohibited by registry policy"); + } +} diff --git a/core/src/main/java/google/registry/model/common/FeatureFlag.java b/core/src/main/java/google/registry/model/common/FeatureFlag.java index ad901645d..3c96e820b 100644 --- a/core/src/main/java/google/registry/model/common/FeatureFlag.java +++ b/core/src/main/java/google/registry/model/common/FeatureFlag.java @@ -62,11 +62,31 @@ public class FeatureFlag extends ImmutableObject implements Buildable { INACTIVE } + /** The names of the feature flags that can be individually set. */ public enum FeatureName { - TEST_FEATURE, - MINIMUM_DATASET_CONTACTS_OPTIONAL, - MINIMUM_DATASET_CONTACTS_PROHIBITED, - INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS + /** Feature flag name used for testing only. */ + TEST_FEATURE(FeatureStatus.INACTIVE), + + /** If we're not requiring the presence of contact data on domain EPP commands. */ + MINIMUM_DATASET_CONTACTS_OPTIONAL(FeatureStatus.INACTIVE), + + /** If we're not permitting the presence of contact data on any EPP commands. */ + MINIMUM_DATASET_CONTACTS_PROHIBITED(FeatureStatus.INACTIVE), + + /** + * If we're including the upcoming domain drop date in the exported list of registered domains. + */ + INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS(FeatureStatus.INACTIVE); + + private final FeatureStatus defaultStatus; + + FeatureName(FeatureStatus defaultStatus) { + this.defaultStatus = defaultStatus; + } + + FeatureStatus getDefaultStatus() { + return this.defaultStatus; + } } /** The name of the flag/feature. */ @@ -155,24 +175,24 @@ public class FeatureFlag extends ImmutableObject implements Buildable { return status.getValueAtTime(time); } - /** Returns if the flag is active, or the default value if the flag does not exist. */ - public static boolean isActiveNowOrElse(FeatureName featureName, boolean defaultValue) { - tm().assertInTransaction(); - return CACHE - .get(featureName) - .map(flag -> flag.getStatus(tm().getTransactionTime()).equals(ACTIVE)) - .orElse(defaultValue); - } - - /** Returns if the FeatureFlag with the given FeatureName is active now. */ + /** + * Returns whether the flag is active now, or else the flag's default value if it doesn't exist. + */ public static boolean isActiveNow(FeatureName featureName) { tm().assertInTransaction(); return isActiveAt(featureName, tm().getTransactionTime()); } - /** Returns if the FeatureFlag with the given FeatureName is active at a given time. */ + /** + * Returns whether the flag is active at the given time, or else the flag's default value if it + * doesn't exist. + */ public static boolean isActiveAt(FeatureName featureName, DateTime dateTime) { - return FeatureFlag.get(featureName).getStatus(dateTime).equals(ACTIVE); + tm().assertInTransaction(); + return CACHE + .get(featureName) + .map(flag -> flag.getStatus(dateTime).equals(ACTIVE)) + .orElse(featureName.getDefaultStatus().equals(ACTIVE)); } @Override diff --git a/core/src/main/java/google/registry/tools/CreateDomainCommand.java b/core/src/main/java/google/registry/tools/CreateDomainCommand.java index dc408f0cf..c3085f034 100644 --- a/core/src/main/java/google/registry/tools/CreateDomainCommand.java +++ b/core/src/main/java/google/registry/tools/CreateDomainCommand.java @@ -16,6 +16,8 @@ package google.registry.tools; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Strings.isNullOrEmpty; +import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL; +import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName; import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; @@ -62,8 +64,8 @@ final class CreateDomainCommand extends CreateOrUpdateDomainCommand { protected void initMutatingEppToolCommand() { tm().transact( () -> { - if (!FeatureFlag.isActiveNowOrElse( - FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL, false)) { + if (!FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL) + && !FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)) { checkArgumentNotNull(registrant, "Registrant must be specified"); checkArgument(!admins.isEmpty(), "At least one admin must be specified"); checkArgument(!techs.isEmpty(), "At least one tech must be specified"); diff --git a/core/src/test/java/google/registry/flows/contact/ContactCreateFlowTest.java b/core/src/test/java/google/registry/flows/contact/ContactCreateFlowTest.java index bc758d63f..4b2232b4c 100644 --- a/core/src/test/java/google/registry/flows/contact/ContactCreateFlowTest.java +++ b/core/src/test/java/google/registry/flows/contact/ContactCreateFlowTest.java @@ -15,6 +15,9 @@ package google.registry.flows.contact; import static com.google.common.truth.Truth.assertThat; +import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED; +import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE; +import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE; import static google.registry.testing.ContactSubject.assertAboutContacts; import static google.registry.testing.DatabaseHelper.assertNoBillingEvents; import static google.registry.testing.DatabaseHelper.newContact; @@ -22,15 +25,19 @@ import static google.registry.testing.DatabaseHelper.persistActiveContact; import static google.registry.testing.DatabaseHelper.persistDeletedContact; import static google.registry.testing.DatabaseHelper.persistResource; import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions; +import static google.registry.util.DateTimeUtils.START_OF_TIME; import static org.junit.jupiter.api.Assertions.assertThrows; +import com.google.common.collect.ImmutableSortedMap; import google.registry.flows.EppException; import google.registry.flows.FlowUtils.NotLoggedInException; import google.registry.flows.ResourceFlowTestCase; import google.registry.flows.contact.ContactFlowUtils.BadInternationalizedPostalInfoException; import google.registry.flows.contact.ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException; +import google.registry.flows.exceptions.ContactsProhibitedException; import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientException; import google.registry.flows.exceptions.ResourceCreateContentionException; +import google.registry.model.common.FeatureFlag; import google.registry.model.contact.Contact; import org.joda.time.DateTime; import org.junit.jupiter.api.Test; @@ -89,6 +96,18 @@ class ContactCreateFlowTest extends ResourceFlowTestCase> nameservers = new ImmutableSet.Builder<>(); for (int i = 1; i < 15; i++) { @@ -1540,8 +1555,8 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase { - assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, false)).isFalse(); - assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, true)).isFalse(); + assertThat(FeatureFlag.isActiveNow(TEST_FEATURE)).isFalse(); }); fakeClock.advanceBy(Duration.standardDays(365)); tm().transact( () -> { - assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, false)).isTrue(); - assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, true)).isTrue(); - }); - } - - @Test - void testSuccess_default_doesNotExist() { - tm().transact( - () -> { - assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, false)).isFalse(); - assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, true)).isTrue(); + assertThat(FeatureFlag.isActiveNow(TEST_FEATURE)).isTrue(); }); } } diff --git a/core/src/test/java/google/registry/tools/CreateDomainCommandTest.java b/core/src/test/java/google/registry/tools/CreateDomainCommandTest.java index 000486cae..9da1db398 100644 --- a/core/src/test/java/google/registry/tools/CreateDomainCommandTest.java +++ b/core/src/test/java/google/registry/tools/CreateDomainCommandTest.java @@ -16,6 +16,7 @@ package google.registry.tools; import static com.google.common.truth.Truth.assertThat; import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL; +import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED; import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.testing.DatabaseHelper.createTld; @@ -115,7 +116,7 @@ class CreateDomainCommandTest extends EppToolCommandTestCase + + + + example.tld + 2 + + ns1.example.net + ns2.example.net + + + 2fooBAR + + + + ABC-12345 + + diff --git a/core/src/test/resources/google/registry/flows/domain/domain_create_response_eap_fee.xml b/core/src/test/resources/google/registry/flows/domain/domain_create_response_eap_fee.xml index 5b9397f67..f573fb789 100644 --- a/core/src/test/resources/google/registry/flows/domain/domain_create_response_eap_fee.xml +++ b/core/src/test/resources/google/registry/flows/domain/domain_create_response_eap_fee.xml @@ -15,7 +15,7 @@ USD 24.00 - 100.00 + 100.00 diff --git a/core/src/test/resources/google/registry/flows/domain/domain_create_response_premium_eap.xml b/core/src/test/resources/google/registry/flows/domain/domain_create_response_premium_eap.xml index 2854b478c..b5bdbc0bf 100644 --- a/core/src/test/resources/google/registry/flows/domain/domain_create_response_premium_eap.xml +++ b/core/src/test/resources/google/registry/flows/domain/domain_create_response_premium_eap.xml @@ -15,7 +15,7 @@ USD 200.00 - 100.00 + 100.00 diff --git a/core/src/test/resources/google/registry/flows/domain/domain_update_remove_all_contacts.xml b/core/src/test/resources/google/registry/flows/domain/domain_update_remove_all_contacts.xml new file mode 100644 index 000000000..93c25ccf1 --- /dev/null +++ b/core/src/test/resources/google/registry/flows/domain/domain_update_remove_all_contacts.xml @@ -0,0 +1,20 @@ + + + + + example.tld + + + sh8013 + sh8013 + sh8013 + + + + + + + ABC-12345 + +