mirror of
https://github.com/google/nomulus
synced 2026-02-20 20:09:09 +00:00
Compare commits
28 Commits
proxy-2026
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7f2db177b | ||
|
|
6747cc894d | ||
|
|
e4c4149033 | ||
|
|
e24c90fea6 | ||
|
|
8ff4d7dc8a | ||
|
|
88906f1bd9 | ||
|
|
bca05f3982 | ||
|
|
763630bca5 | ||
|
|
140b19e919 | ||
|
|
a787660b27 | ||
|
|
4aadcf818a | ||
|
|
ab29e481fa | ||
|
|
f2f9694a94 | ||
|
|
3f8145b44f | ||
|
|
1fdacf25dc | ||
|
|
41d26d8385 | ||
|
|
71c9407f07 | ||
|
|
a138806199 | ||
|
|
a5c1412aac | ||
|
|
41393e5f8d | ||
|
|
a7387e975b | ||
|
|
5c6667507b | ||
|
|
c187c92ae4 | ||
|
|
22ca4e3f2b | ||
|
|
f27136458a | ||
|
|
d8e647316e | ||
|
|
d6e0a7b979 | ||
|
|
5725eb95e0 |
@@ -98,8 +98,8 @@ PRESUBMITS = {
|
||||
"File did not include the license header.",
|
||||
|
||||
# Files must end in a newline
|
||||
PresubmitCheck(r".*\n$", ("java", "js", "soy", "sql", "py", "sh", "gradle", "ts"),
|
||||
{"node_modules/"}, REQUIRED):
|
||||
PresubmitCheck(r".*\n$", ("java", "js", "soy", "sql", "py", "sh", "gradle", "ts", "xml"),
|
||||
{"node_modules/", ".idea"}, REQUIRED):
|
||||
"Source files must end in a newline.",
|
||||
|
||||
# System.(out|err).println should only appear in tools/ or load-testing/
|
||||
|
||||
@@ -569,11 +569,6 @@ if (environment == 'alpha') {
|
||||
mainClass: 'google.registry.beam.resave.ResaveAllEppResourcesPipeline',
|
||||
metaData: 'google/registry/beam/resave_all_epp_resources_pipeline_metadata.json'
|
||||
],
|
||||
wipeOutContactHistoryPii:
|
||||
[
|
||||
mainClass: 'google.registry.beam.wipeout.WipeOutContactHistoryPiiPipeline',
|
||||
metaData: 'google/registry/beam/wipe_out_contact_history_pii_pipeline_metadata.json'
|
||||
],
|
||||
]
|
||||
project.tasks.create("stageBeamPipelines") {
|
||||
doLast {
|
||||
|
||||
@@ -131,12 +131,6 @@ public class BatchModule {
|
||||
return extractOptionalDatetimeParameter(req, ExpandBillingRecurrencesAction.PARAM_END_TIME);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(WipeOutContactHistoryPiiAction.PARAM_CUTOFF_TIME)
|
||||
static Optional<DateTime> provideCutoffTime(HttpServletRequest req) {
|
||||
return extractOptionalDatetimeParameter(req, WipeOutContactHistoryPiiAction.PARAM_CUTOFF_TIME);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(ExpandBillingRecurrencesAction.PARAM_ADVANCE_CURSOR)
|
||||
static boolean provideAdvanceCursor(HttpServletRequest req) {
|
||||
|
||||
@@ -29,7 +29,6 @@ import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.flows.poll.PollFlowUtils;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.EppResourceUtils;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
@@ -94,7 +93,6 @@ public class DeleteLoadTestDataAction implements Runnable {
|
||||
TRANSACTION_REPEATABLE_READ,
|
||||
() -> {
|
||||
LOAD_TEST_REGISTRARS.forEach(this::deletePollMessages);
|
||||
tm().loadAllOfStream(Contact.class).forEach(this::deleteContact);
|
||||
tm().loadAllOfStream(Host.class).forEach(this::deleteHost);
|
||||
});
|
||||
}
|
||||
@@ -110,21 +108,6 @@ public class DeleteLoadTestDataAction implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteContact(Contact contact) {
|
||||
if (!LOAD_TEST_REGISTRARS.contains(contact.getPersistedCurrentSponsorRegistrarId())) {
|
||||
return;
|
||||
}
|
||||
// We cannot remove contacts from domains in the general case, so we cannot delete contacts
|
||||
// that are linked to domains (since it would break the foreign keys)
|
||||
if (EppResourceUtils.isLinked(contact.createVKey(), clock.nowUtc())) {
|
||||
logger.atWarning().log(
|
||||
"Cannot delete contact with repo ID %s since it is referenced from a domain.",
|
||||
contact.getRepoId());
|
||||
return;
|
||||
}
|
||||
deleteResource(contact);
|
||||
}
|
||||
|
||||
private void deleteHost(Host host) {
|
||||
if (!LOAD_TEST_REGISTRARS.contains(host.getPersistedCurrentSponsorRegistrarId())) {
|
||||
return;
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
// 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.batch;
|
||||
|
||||
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
|
||||
import static google.registry.flows.FlowUtils.marshalWithLenientRetry;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.ResourceUtils.readResourceUtf8;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_NO_CONTENT;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.util.concurrent.RateLimiter;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.flows.EppController;
|
||||
import google.registry.flows.EppRequestSource;
|
||||
import google.registry.flows.PasswordOnlyTransportCredentials;
|
||||
import google.registry.flows.StatelessRequestSessionMetadata;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.eppcommon.ProtocolDefinition;
|
||||
import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Level;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* An action that removes all contacts from all active (non-deleted) domains.
|
||||
*
|
||||
* <p>This implements part 1 of phase 3 of the Minimum Dataset migration, wherein we remove all uses
|
||||
* of contact objects in preparation for later removing all contact data from the system.
|
||||
*
|
||||
* <p>This runs as a singly threaded, resumable action that loads batches of domains still
|
||||
* containing contacts, and runs a superuser domain update on each one to remove the contacts,
|
||||
* leaving behind a record recording that update.
|
||||
*/
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
path = RemoveAllDomainContactsAction.PATH,
|
||||
method = Action.Method.POST,
|
||||
auth = Auth.AUTH_ADMIN)
|
||||
public class RemoveAllDomainContactsAction implements Runnable {
|
||||
|
||||
public static final String PATH = "/_dr/task/removeAllDomainContacts";
|
||||
private static final String LOCK_NAME = "Remove all domain contacts";
|
||||
private static final String CONTACT_FMT = "<domain:contact type=\"%s\">%s</domain:contact>";
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private final EppController eppController;
|
||||
private final String registryAdminClientId;
|
||||
private final LockHandler lockHandler;
|
||||
private final RateLimiter rateLimiter;
|
||||
private final Response response;
|
||||
private final String updateDomainXml;
|
||||
private int successes = 0;
|
||||
private int failures = 0;
|
||||
|
||||
private static final int BATCH_SIZE = 10000;
|
||||
|
||||
@Inject
|
||||
RemoveAllDomainContactsAction(
|
||||
EppController eppController,
|
||||
@Config("registryAdminClientId") String registryAdminClientId,
|
||||
LockHandler lockHandler,
|
||||
@Named("standardRateLimiter") RateLimiter rateLimiter,
|
||||
Response response) {
|
||||
this.eppController = eppController;
|
||||
this.registryAdminClientId = registryAdminClientId;
|
||||
this.lockHandler = lockHandler;
|
||||
this.rateLimiter = rateLimiter;
|
||||
this.response = response;
|
||||
this.updateDomainXml =
|
||||
readResourceUtf8(RemoveAllDomainContactsAction.class, "domain_remove_contacts.xml");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
response.setContentType(PLAIN_TEXT_UTF_8);
|
||||
Callable<Void> runner =
|
||||
() -> {
|
||||
try {
|
||||
runLocked();
|
||||
response.setStatus(SC_OK);
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log("Errored out during execution.");
|
||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
response.setPayload(String.format("Errored out with cause: %s", e));
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
if (!lockHandler.executeWithLocks(runner, null, Duration.standardHours(1), LOCK_NAME)) {
|
||||
// Send a 200-series status code to prevent this conflicting action from retrying.
|
||||
response.setStatus(SC_NO_CONTENT);
|
||||
response.setPayload("Could not acquire lock; already running?");
|
||||
}
|
||||
}
|
||||
|
||||
private void runLocked() {
|
||||
logger.atInfo().log("Removing contacts on all active domains.");
|
||||
|
||||
List<String> domainRepoIdsBatch;
|
||||
do {
|
||||
domainRepoIdsBatch =
|
||||
tm().<List<String>>transact(
|
||||
() ->
|
||||
tm().getEntityManager()
|
||||
.createQuery(
|
||||
"""
|
||||
SELECT repoId FROM Domain WHERE deletionTime = :end_of_time AND NOT (
|
||||
adminContact IS NULL AND billingContact IS NULL
|
||||
AND registrantContact IS NULL AND techContact IS NULL)
|
||||
""")
|
||||
.setParameter("end_of_time", END_OF_TIME)
|
||||
.setMaxResults(BATCH_SIZE)
|
||||
.getResultList());
|
||||
|
||||
for (String domainRepoId : domainRepoIdsBatch) {
|
||||
rateLimiter.acquire();
|
||||
runDomainUpdateFlow(domainRepoId);
|
||||
}
|
||||
} while (!domainRepoIdsBatch.isEmpty());
|
||||
String msg =
|
||||
String.format(
|
||||
"Finished; %d domains were successfully updated and %d errored out.",
|
||||
successes, failures);
|
||||
logger.at(failures == 0 ? Level.INFO : Level.WARNING).log(msg);
|
||||
response.setPayload(msg);
|
||||
}
|
||||
|
||||
private void runDomainUpdateFlow(String repoId) {
|
||||
// Create a new transaction that the flow's execution will be enlisted in that loads the domain
|
||||
// transactionally. This way we can ensure that nothing else has modified the domain in question
|
||||
// in the intervening period since the query above found it. If a single domain update fails
|
||||
// permanently, log it and move on to not block processing all the other domains.
|
||||
try {
|
||||
boolean success = tm().transact(() -> runDomainUpdateFlowInner(repoId));
|
||||
if (success) {
|
||||
successes++;
|
||||
} else {
|
||||
failures++;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
logger.atWarning().withCause(t).log(
|
||||
"Failed updating domain with repoId %s; skipping.", repoId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the actual domain update flow and returns whether the contact removals were successful.
|
||||
*/
|
||||
private boolean runDomainUpdateFlowInner(String repoId) {
|
||||
Domain domain = tm().loadByKey(VKey.create(Domain.class, repoId));
|
||||
if (!domain.getDeletionTime().equals(END_OF_TIME)) {
|
||||
// Domain has been deleted since the action began running; nothing further to be
|
||||
// done here.
|
||||
logger.atInfo().log("Nothing to process for deleted domain '%s'.", domain.getDomainName());
|
||||
return false;
|
||||
}
|
||||
logger.atInfo().log("Attempting to remove contacts on domain '%s'.", domain.getDomainName());
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
ImmutableMap<VKey<? extends Contact>, Contact> contacts =
|
||||
tm().loadByKeys(
|
||||
domain.getContacts().stream()
|
||||
.map(DesignatedContact::getContactKey)
|
||||
.collect(ImmutableSet.toImmutableSet()));
|
||||
|
||||
// Collect all the (non-registrant) contacts referenced by the domain and compile an EPP XML
|
||||
// string that removes each one.
|
||||
for (DesignatedContact designatedContact : domain.getContacts()) {
|
||||
@Nullable Contact contact = contacts.get(designatedContact.getContactKey());
|
||||
if (contact == null) {
|
||||
logger.atWarning().log(
|
||||
"Domain '%s' referenced contact with repo ID '%s' that couldn't be" + " loaded.",
|
||||
domain.getDomainName(), designatedContact.getContactKey().getKey());
|
||||
continue;
|
||||
}
|
||||
sb.append(
|
||||
String.format(
|
||||
CONTACT_FMT,
|
||||
Ascii.toLowerCase(designatedContact.getType().name()),
|
||||
contact.getContactId()))
|
||||
.append("\n");
|
||||
}
|
||||
|
||||
String compiledXml =
|
||||
updateDomainXml
|
||||
.replace("%DOMAIN%", domain.getDomainName())
|
||||
.replace("%CONTACTS%", sb.toString());
|
||||
EppOutput output =
|
||||
eppController.handleEppCommand(
|
||||
new StatelessRequestSessionMetadata(
|
||||
registryAdminClientId, ProtocolDefinition.getVisibleServiceExtensionUris()),
|
||||
new PasswordOnlyTransportCredentials(),
|
||||
EppRequestSource.BACKEND,
|
||||
false,
|
||||
true,
|
||||
compiledXml.getBytes(US_ASCII));
|
||||
if (output.isSuccess()) {
|
||||
logger.atInfo().log(
|
||||
"Successfully removed contacts from domain '%s'.", domain.getDomainName());
|
||||
} else {
|
||||
logger.atWarning().log(
|
||||
"Failed removing contacts from domain '%s' with error %s.",
|
||||
domain.getDomainName(), new String(marshalWithLenientRetry(output), US_ASCII));
|
||||
}
|
||||
return output.isSuccess();
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
// Copyright 2021 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.batch;
|
||||
|
||||
import static google.registry.beam.BeamUtils.createJobName;
|
||||
import static google.registry.request.RequestParameters.PARAM_DRY_RUN;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.api.services.dataflow.Dataflow;
|
||||
import com.google.api.services.dataflow.model.LaunchFlexTemplateParameter;
|
||||
import com.google.api.services.dataflow.model.LaunchFlexTemplateRequest;
|
||||
import com.google.api.services.dataflow.model.LaunchFlexTemplateResponse;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.beam.wipeout.WipeOutContactHistoryPiiPipeline;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import jakarta.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* An action that launches {@link WipeOutContactHistoryPiiPipeline} to wipe out Personal
|
||||
* Identifiable Information (PII) fields of {@link ContactHistory} entities.
|
||||
*
|
||||
* <p>{@link ContactHistory} entities should be retained in the database for only certain amount of
|
||||
* time.
|
||||
*/
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
path = WipeOutContactHistoryPiiAction.PATH,
|
||||
auth = Auth.AUTH_ADMIN)
|
||||
public class WipeOutContactHistoryPiiAction implements Runnable {
|
||||
|
||||
public static final String PATH = "/_dr/task/wipeOutContactHistoryPii";
|
||||
public static final String PARAM_CUTOFF_TIME = "wipeoutTime";
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final String PIPELINE_NAME = "wipe_out_contact_history_pii_pipeline";
|
||||
|
||||
private final Clock clock;
|
||||
private final boolean isDryRun;
|
||||
private final Optional<DateTime> maybeCutoffTime;
|
||||
private final int minMonthsBeforeWipeOut;
|
||||
private final String stagingBucketUrl;
|
||||
private final String projectId;
|
||||
private final String jobRegion;
|
||||
private final Dataflow dataflow;
|
||||
private final Response response;
|
||||
|
||||
@Inject
|
||||
public WipeOutContactHistoryPiiAction(
|
||||
Clock clock,
|
||||
@Parameter(PARAM_DRY_RUN) boolean isDryRun,
|
||||
@Parameter(PARAM_CUTOFF_TIME) Optional<DateTime> maybeCutoffTime,
|
||||
@Config("minMonthsBeforeWipeOut") int minMonthsBeforeWipeOut,
|
||||
@Config("beamStagingBucketUrl") String stagingBucketUrl,
|
||||
@Config("projectId") String projectId,
|
||||
@Config("defaultJobRegion") String jobRegion,
|
||||
Dataflow dataflow,
|
||||
Response response) {
|
||||
this.clock = clock;
|
||||
this.isDryRun = isDryRun;
|
||||
this.maybeCutoffTime = maybeCutoffTime;
|
||||
this.minMonthsBeforeWipeOut = minMonthsBeforeWipeOut;
|
||||
this.stagingBucketUrl = stagingBucketUrl;
|
||||
this.projectId = projectId;
|
||||
this.jobRegion = jobRegion;
|
||||
this.dataflow = dataflow;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
||||
DateTime cutoffTime =
|
||||
maybeCutoffTime.orElse(clock.nowUtc().minusMonths(minMonthsBeforeWipeOut));
|
||||
LaunchFlexTemplateParameter launchParameter =
|
||||
new LaunchFlexTemplateParameter()
|
||||
.setJobName(
|
||||
createJobName(
|
||||
String.format(
|
||||
"contact-history-pii-wipeout-%s",
|
||||
cutoffTime.toString("yyyy-MM-dd't'HH-mm-ss'z'")),
|
||||
clock))
|
||||
.setContainerSpecGcsPath(
|
||||
String.format("%s/%s_metadata.json", stagingBucketUrl, PIPELINE_NAME))
|
||||
.setParameters(
|
||||
ImmutableMap.of(
|
||||
"registryEnvironment",
|
||||
RegistryEnvironment.get().name(),
|
||||
"cutoffTime",
|
||||
cutoffTime.toString("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
|
||||
"isDryRun",
|
||||
Boolean.toString(isDryRun)));
|
||||
logger.atInfo().log(
|
||||
"Launching Beam pipeline to wipe out all PII of contact history entities prior to %s%s.",
|
||||
cutoffTime, " in dry run mode");
|
||||
try {
|
||||
LaunchFlexTemplateResponse launchResponse =
|
||||
dataflow
|
||||
.projects()
|
||||
.locations()
|
||||
.flexTemplates()
|
||||
.launch(
|
||||
projectId,
|
||||
jobRegion,
|
||||
new LaunchFlexTemplateRequest().setLaunchParameter(launchParameter))
|
||||
.execute();
|
||||
logger.atInfo().log("Got response: %s", launchResponse.getJob().toPrettyString());
|
||||
response.setStatus(SC_OK);
|
||||
response.setPayload(
|
||||
String.format(
|
||||
"Launched contact history PII wipeout pipeline: %s",
|
||||
launchResponse.getJob().getId()));
|
||||
} catch (IOException e) {
|
||||
logger.atWarning().withCause(e).log("Pipeline Launch failed");
|
||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
response.setPayload(String.format("Pipeline launch failed: %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,8 +50,6 @@ import google.registry.config.CredentialModule;
|
||||
import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.gcs.GcsUtils;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.host.Host;
|
||||
@@ -73,7 +71,6 @@ import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashSet;
|
||||
import org.apache.beam.sdk.Pipeline;
|
||||
import org.apache.beam.sdk.PipelineResult;
|
||||
import org.apache.beam.sdk.coders.KvCoder;
|
||||
@@ -138,25 +135,21 @@ import org.joda.time.DateTime;
|
||||
* pairs of (contact/host repo ID: pending deposit) for all RDE pending deposits for further
|
||||
* processing.
|
||||
*
|
||||
* <h3>{@link Contact}</h3>
|
||||
*
|
||||
* We first join most recent contact histories, represented by (contact repo ID: contact history
|
||||
* revision ID) pairs, with referenced contacts, represented by (contact repo ID: pending deposit)
|
||||
* pairs, on the contact repo ID, to remove unreferenced contact histories. Contact resources are
|
||||
* then loaded from the remaining referenced contact histories, and marshalled into (pending
|
||||
* deposit: deposit fragment) pairs.
|
||||
*
|
||||
* <h3>{@link Host}</h3>
|
||||
*
|
||||
* Similar to {@link Contact}, we join the most recent host history with referenced hosts to find
|
||||
* most recent referenced hosts. For external hosts we do the same treatment as we did on contacts
|
||||
* and obtain the (pending deposit: deposit fragment) pairs. For subordinate hosts, we need to find
|
||||
* the superordinate domain in order to properly handle pending transfer in the deposit as well. So
|
||||
* we first find the superordinate domain repo ID from the host and join the (superordinate domain
|
||||
* repo ID: (subordinate host repo ID: (pending deposit: revision ID))) pair with the (domain repo
|
||||
* ID: revision ID) pair obtained from the domain history query in order to map the host at
|
||||
* watermark to the domain at watermark. We then proceed to create the (pending deposit: deposit
|
||||
* fragment) pair for subordinate hosts using the added domain information.
|
||||
* <p>We first join most recent host histories, represented by (host repo ID: host history revision
|
||||
* ID) pairs, with referenced hosts, represented by (host repo ID: pending deposit) pairs, on the
|
||||
* host repo ID, to remove unreferenced host histories. Host resources are then loaded from the
|
||||
* remaining referenced host histories, and marshalled into (pending deposit: deposit fragment)
|
||||
* pairs.
|
||||
*
|
||||
* <p>For subordinate hosts, we need to find the superordinate domain in order to properly handle
|
||||
* pending transfer in the deposit as well. So we first find the superordinate domain repo ID from
|
||||
* the host and join the (superordinate domain repo ID: (subordinate host repo ID: (pending deposit:
|
||||
* revision ID))) pair with the (domain repo ID: revision ID) pair obtained from the domain history
|
||||
* query in order to map the host at watermark to the domain at watermark. We then proceed to create
|
||||
* the (pending deposit: deposit fragment) pair for subordinate hosts using the added domain
|
||||
* information.
|
||||
*
|
||||
* <h2>Processing {@link DepositFragment}</h2>
|
||||
*
|
||||
@@ -230,9 +223,6 @@ public class RdePipeline implements Serializable {
|
||||
PCollection<KV<String, Long>> domainHistories =
|
||||
getMostRecentHistoryEntries(pipeline, DomainHistory.class);
|
||||
|
||||
PCollection<KV<String, Long>> contactHistories =
|
||||
getMostRecentHistoryEntries(pipeline, ContactHistory.class);
|
||||
|
||||
PCollection<KV<String, Long>> hostHistories =
|
||||
getMostRecentHistoryEntries(pipeline, HostHistory.class);
|
||||
|
||||
@@ -241,10 +231,6 @@ public class RdePipeline implements Serializable {
|
||||
PCollection<KV<PendingDeposit, DepositFragment>> domainFragments =
|
||||
processedDomainHistories.get(DOMAIN_FRAGMENTS);
|
||||
|
||||
PCollection<KV<PendingDeposit, DepositFragment>> contactFragments =
|
||||
processContactHistories(
|
||||
processedDomainHistories.get(REFERENCED_CONTACTS), contactHistories);
|
||||
|
||||
PCollectionTuple processedHosts =
|
||||
processHostHistories(processedDomainHistories.get(REFERENCED_HOSTS), hostHistories);
|
||||
|
||||
@@ -256,7 +242,6 @@ public class RdePipeline implements Serializable {
|
||||
|
||||
return PCollectionList.of(registrarFragments)
|
||||
.and(domainFragments)
|
||||
.and(contactFragments)
|
||||
.and(externalHostFragments)
|
||||
.and(subordinateHostFragments)
|
||||
.apply(
|
||||
@@ -437,7 +422,6 @@ public class RdePipeline implements Serializable {
|
||||
private PCollectionTuple processDomainHistories(PCollection<KV<String, Long>> domainHistories) {
|
||||
Counter activeDomainCounter = Metrics.counter("RDE", "ActiveDomainBase");
|
||||
Counter domainFragmentCounter = Metrics.counter("RDE", "DomainFragment");
|
||||
Counter referencedContactCounter = Metrics.counter("RDE", "ReferencedContact");
|
||||
Counter referencedHostCounter = Metrics.counter("RDE", "ReferencedHost");
|
||||
return domainHistories.apply(
|
||||
"Map DomainHistory to DepositFragment " + "and emit referenced Contact and Host",
|
||||
@@ -463,19 +447,8 @@ public class RdePipeline implements Serializable {
|
||||
KV.of(
|
||||
pendingDeposit,
|
||||
marshaller.marshalDomain(domain, pendingDeposit.mode())));
|
||||
// Contacts and hosts are only deposited in RDE, not BRDA.
|
||||
// Hosts are only deposited in RDE, not BRDA.
|
||||
if (pendingDeposit.mode() == RdeMode.FULL) {
|
||||
HashSet<Serializable> contacts = new HashSet<>();
|
||||
domain.getAdminContact().ifPresent(c -> contacts.add(c.getKey()));
|
||||
domain.getTechContact().ifPresent(c -> contacts.add(c.getKey()));
|
||||
domain.getRegistrant().ifPresent(c -> contacts.add(c.getKey()));
|
||||
domain.getBillingContact().ifPresent(c -> contacts.add(c.getKey()));
|
||||
referencedContactCounter.inc(contacts.size());
|
||||
contacts.forEach(
|
||||
contactRepoId ->
|
||||
receiver
|
||||
.get(REFERENCED_CONTACTS)
|
||||
.output(KV.of((String) contactRepoId, pendingDeposit)));
|
||||
if (domain.getNsHosts() != null) {
|
||||
referencedHostCounter.inc(domain.getNsHosts().size());
|
||||
domain
|
||||
@@ -497,38 +470,6 @@ public class RdePipeline implements Serializable {
|
||||
DOMAIN_FRAGMENTS, TupleTagList.of(REFERENCED_CONTACTS).and(REFERENCED_HOSTS)));
|
||||
}
|
||||
|
||||
private PCollection<KV<PendingDeposit, DepositFragment>> processContactHistories(
|
||||
PCollection<KV<String, PendingDeposit>> referencedContacts,
|
||||
PCollection<KV<String, Long>> contactHistories) {
|
||||
Counter contactFragmentCounter = Metrics.counter("RDE", "ContactFragment");
|
||||
return removeUnreferencedResource(referencedContacts, contactHistories, Contact.class)
|
||||
.apply(
|
||||
"Map Contact to DepositFragment",
|
||||
FlatMapElements.into(
|
||||
kvs(
|
||||
TypeDescriptor.of(PendingDeposit.class),
|
||||
TypeDescriptor.of(DepositFragment.class)))
|
||||
.via(
|
||||
(KV<String, CoGbkResult> kv) -> {
|
||||
Contact contact =
|
||||
(Contact)
|
||||
loadResourceByHistoryEntryId(
|
||||
ContactHistory.class,
|
||||
kv.getKey(),
|
||||
kv.getValue().getAll(REVISION_ID));
|
||||
DepositFragment fragment = marshaller.marshalContact(contact);
|
||||
ImmutableSet<KV<PendingDeposit, DepositFragment>> fragments =
|
||||
Streams.stream(kv.getValue().getAll(PENDING_DEPOSIT))
|
||||
// The same contact could be used by multiple domains, therefore
|
||||
// matched to the same pending deposit multiple times.
|
||||
.distinct()
|
||||
.map(pendingDeposit -> KV.of(pendingDeposit, fragment))
|
||||
.collect(toImmutableSet());
|
||||
contactFragmentCounter.inc(fragments.size());
|
||||
return fragments;
|
||||
}));
|
||||
}
|
||||
|
||||
private PCollectionTuple processHostHistories(
|
||||
PCollection<KV<String, PendingDeposit>> referencedHosts,
|
||||
PCollection<KV<String, Long>> hostHistories) {
|
||||
|
||||
@@ -25,7 +25,6 @@ import com.google.common.collect.Streams;
|
||||
import google.registry.beam.common.RegistryJpaIO;
|
||||
import google.registry.beam.common.RegistryJpaIO.Read;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.host.Host;
|
||||
@@ -56,7 +55,7 @@ import org.joda.time.DateTime;
|
||||
public class ResaveAllEppResourcesPipeline implements Serializable {
|
||||
|
||||
private static final ImmutableSet<Class<? extends EppResource>> EPP_RESOURCE_CLASSES =
|
||||
ImmutableSet.of(Contact.class, Domain.class, Host.class);
|
||||
ImmutableSet.of(Domain.class, Host.class);
|
||||
|
||||
/**
|
||||
* There exist three possible situations where we know we'll want to project domains to the
|
||||
@@ -92,25 +91,12 @@ public class ResaveAllEppResourcesPipeline implements Serializable {
|
||||
|
||||
void setupPipeline(Pipeline pipeline) {
|
||||
if (options.getFast()) {
|
||||
fastResaveContacts(pipeline);
|
||||
fastResaveDomains(pipeline);
|
||||
} else {
|
||||
EPP_RESOURCE_CLASSES.forEach(clazz -> forceResaveAllResources(pipeline, clazz));
|
||||
}
|
||||
}
|
||||
|
||||
/** Projects to the current time and saves any contacts with expired transfers. */
|
||||
private void fastResaveContacts(Pipeline pipeline) {
|
||||
Read<String, String> repoIdRead =
|
||||
RegistryJpaIO.read(
|
||||
"SELECT repoId FROM Contact WHERE transferData.transferStatus = 'PENDING' AND"
|
||||
+ " transferData.pendingTransferExpirationTime < current_timestamp()",
|
||||
String.class,
|
||||
r -> r)
|
||||
.withCoder(StringUtf8Coder.of());
|
||||
projectAndResaveResources(pipeline, Contact.class, repoIdRead);
|
||||
}
|
||||
|
||||
/**
|
||||
* Projects to the current time and saves any domains with expired pending actions (e.g.
|
||||
* transfers, grace periods).
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
// 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.beam.wipeout;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static org.apache.beam.sdk.values.TypeDescriptors.voids;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Streams;
|
||||
import google.registry.beam.common.RegistryJpaIO;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.io.Serializable;
|
||||
import org.apache.beam.sdk.Pipeline;
|
||||
import org.apache.beam.sdk.PipelineResult;
|
||||
import org.apache.beam.sdk.coders.KvCoder;
|
||||
import org.apache.beam.sdk.coders.StringUtf8Coder;
|
||||
import org.apache.beam.sdk.coders.VarLongCoder;
|
||||
import org.apache.beam.sdk.metrics.Counter;
|
||||
import org.apache.beam.sdk.metrics.Metrics;
|
||||
import org.apache.beam.sdk.options.PipelineOptionsFactory;
|
||||
import org.apache.beam.sdk.transforms.MapElements;
|
||||
import org.apache.beam.sdk.transforms.join.CoGroupByKey;
|
||||
import org.apache.beam.sdk.transforms.join.KeyedPCollectionTuple;
|
||||
import org.apache.beam.sdk.values.KV;
|
||||
import org.apache.beam.sdk.values.PCollection;
|
||||
import org.apache.beam.sdk.values.TupleTag;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Definition of a Dataflow Flex pipeline template, which finds out {@link ContactHistory} entries
|
||||
* that are older than a given age (excluding the most recent one, even if it falls with the range)
|
||||
* and wipe out PII information in them.
|
||||
*
|
||||
* <p>To stage this template locally, run {@code ./nom_build :core:sBP --environment=alpha \
|
||||
* --pipeline=wipeOutContactHistoryPii}.
|
||||
*
|
||||
* <p>Then, you can run the staged template via the API client library, gCloud or a raw REST call.
|
||||
*/
|
||||
public class WipeOutContactHistoryPiiPipeline implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -4111052675715913820L;
|
||||
private static final TupleTag<Long> REVISIONS_TO_WIPE = new TupleTag<>();
|
||||
private static final TupleTag<Long> MOST_RECENT_REVISION = new TupleTag<>();
|
||||
|
||||
private final DateTime cutoffTime;
|
||||
private final boolean dryRun;
|
||||
private final Counter contactsInScope =
|
||||
Metrics.counter("WipeOutContactHistoryPii", "contacts in scope");
|
||||
private final Counter historiesToWipe =
|
||||
Metrics.counter("WipeOutContactHistoryPii", "contact histories to wipe PII from");
|
||||
private final Counter historiesWiped =
|
||||
Metrics.counter("WipeOutContactHistoryPii", "contact histories actually updated");
|
||||
|
||||
WipeOutContactHistoryPiiPipeline(WipeOutContactHistoryPiiPipelineOptions options) {
|
||||
dryRun = options.getIsDryRun();
|
||||
cutoffTime = DateTime.parse(options.getCutoffTime());
|
||||
}
|
||||
|
||||
void setup(Pipeline pipeline) {
|
||||
KeyedPCollectionTuple.of(REVISIONS_TO_WIPE, getHistoryEntriesToWipe(pipeline))
|
||||
.and(MOST_RECENT_REVISION, getMostRecentHistoryEntries(pipeline))
|
||||
.apply("Group by contact", CoGroupByKey.create())
|
||||
.apply(
|
||||
"Wipe out PII",
|
||||
MapElements.into(voids())
|
||||
.via(
|
||||
kv -> {
|
||||
String repoId = kv.getKey();
|
||||
long mostRecentRevision = kv.getValue().getOnly(MOST_RECENT_REVISION);
|
||||
ImmutableList<Long> revisionsToWipe =
|
||||
Streams.stream(kv.getValue().getAll(REVISIONS_TO_WIPE))
|
||||
.filter(e -> e != mostRecentRevision)
|
||||
.collect(toImmutableList());
|
||||
if (revisionsToWipe.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
contactsInScope.inc();
|
||||
tm().transact(
|
||||
() -> {
|
||||
for (long revisionId : revisionsToWipe) {
|
||||
historiesToWipe.inc();
|
||||
ContactHistory history =
|
||||
tm().loadByKey(
|
||||
VKey.create(
|
||||
ContactHistory.class,
|
||||
new HistoryEntryId(repoId, revisionId)));
|
||||
// In the unlikely case where multiple pipelines run at the
|
||||
// same time, or where the runner decides to rerun a particular
|
||||
// transform, we might have a history entry that has already been
|
||||
// wiped at this point. There's no need to wipe it again.
|
||||
if (!dryRun
|
||||
&& history.getContactBase().isPresent()
|
||||
&& history.getContactBase().get().getEmailAddress() != null) {
|
||||
historiesWiped.inc();
|
||||
tm().update(history.asBuilder().wipeOutPii().build());
|
||||
}
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}));
|
||||
}
|
||||
|
||||
PCollection<KV<String, Long>> getHistoryEntriesToWipe(Pipeline pipeline) {
|
||||
return pipeline.apply(
|
||||
"Find contact histories to wipee",
|
||||
// Email is one of the required fields in EPP, meaning it's initially not null when it
|
||||
// is set by EPP flows (even though it is nullalbe in the SQL schema). Therefore,
|
||||
// checking if it's null is one way to avoid processing contact history entities that
|
||||
// have been processed previously. Refer to RFC 5733 for more information.
|
||||
RegistryJpaIO.read(
|
||||
"SELECT repoId, revisionId FROM ContactHistory WHERE resource.email IS NOT NULL"
|
||||
+ " AND modificationTime < :cutoffTime",
|
||||
ImmutableMap.of("cutoffTime", cutoffTime),
|
||||
Object[].class,
|
||||
row -> KV.of((String) row[0], (long) row[1]))
|
||||
.withCoder(KvCoder.of(StringUtf8Coder.of(), VarLongCoder.of())));
|
||||
}
|
||||
|
||||
PCollection<KV<String, Long>> getMostRecentHistoryEntries(Pipeline pipeline) {
|
||||
return pipeline.apply(
|
||||
"Find the most recent historiy entry for each contact",
|
||||
RegistryJpaIO.read(
|
||||
"SELECT repoId, revisionId FROM ContactHistory"
|
||||
+ " WHERE (repoId, modificationTime) IN"
|
||||
+ " (SELECT repoId, MAX(modificationTime) FROM ContactHistory GROUP BY repoId)",
|
||||
ImmutableMap.of(),
|
||||
Object[].class,
|
||||
row -> KV.of((String) row[0], (long) row[1]))
|
||||
.withCoder(KvCoder.of(StringUtf8Coder.of(), VarLongCoder.of())));
|
||||
}
|
||||
|
||||
PipelineResult run(Pipeline pipeline) {
|
||||
setup(pipeline);
|
||||
return pipeline.run();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
PipelineOptionsFactory.register(WipeOutContactHistoryPiiPipelineOptions.class);
|
||||
WipeOutContactHistoryPiiPipelineOptions options =
|
||||
PipelineOptionsFactory.fromArgs(args)
|
||||
.withValidation()
|
||||
.as(WipeOutContactHistoryPiiPipelineOptions.class);
|
||||
// Repeatable read should be more than enough since we are dealing with old history entries that
|
||||
// are otherwise immutable.
|
||||
options.setIsolationOverride(TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ);
|
||||
Pipeline pipeline = Pipeline.create(options);
|
||||
new WipeOutContactHistoryPiiPipeline(options).run(pipeline);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// 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.beam.wipeout;
|
||||
|
||||
import google.registry.beam.common.RegistryPipelineOptions;
|
||||
import org.apache.beam.sdk.options.Default;
|
||||
import org.apache.beam.sdk.options.Description;
|
||||
|
||||
public interface WipeOutContactHistoryPiiPipelineOptions extends RegistryPipelineOptions {
|
||||
|
||||
@Description(
|
||||
"A contact history entry with a history modification time before this time will have its PII"
|
||||
+ " wiped, unless it is the most entry for the contact.")
|
||||
String getCutoffTime();
|
||||
|
||||
void setCutoffTime(String value);
|
||||
|
||||
@Description(
|
||||
"If true, the wiped out billing events will not be saved but the pipeline metrics counter"
|
||||
+ " will still be updated.")
|
||||
@Default.Boolean(false)
|
||||
boolean getIsDryRun();
|
||||
|
||||
void setIsDryRun(boolean value);
|
||||
}
|
||||
@@ -143,15 +143,15 @@ public final class RegistryConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the roid suffix to be used for the roids of all contacts and hosts. E.g. a value of
|
||||
* "ROID" would end up creating roids that look like "ABC123-ROID".
|
||||
* Returns the roid suffix to be used for the roids of all hosts. E.g. a value of "ROID" would
|
||||
* end up creating roids that look like "ABC123-ROID".
|
||||
*
|
||||
* @see <a href="http://www.iana.org/assignments/epp-repository-ids/epp-repository-ids.xhtml">
|
||||
* Extensible Provisioning Protocol (EPP) Repository Identifiers</a>
|
||||
*/
|
||||
@Provides
|
||||
@Config("contactAndHostRoidSuffix")
|
||||
public static String provideContactAndHostRoidSuffix(RegistryConfigSettings config) {
|
||||
@Config("hostRoidSuffix")
|
||||
public static String provideHostRoidSuffix(RegistryConfigSettings config) {
|
||||
return config.registryPolicy.contactAndHostRoidSuffix;
|
||||
}
|
||||
|
||||
@@ -1024,18 +1024,6 @@ public final class RegistryConfig {
|
||||
return Duration.standardSeconds(config.monitoring.writeIntervalSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* The global automatic transfer length for contacts. After this amount of time has elapsed, the
|
||||
* transfer is automatically approved.
|
||||
*
|
||||
* @see google.registry.flows.contact.ContactTransferRequestFlow
|
||||
*/
|
||||
@Provides
|
||||
@Config("contactAutomaticTransferLength")
|
||||
public static Duration provideContactAutomaticTransferLength(RegistryConfigSettings config) {
|
||||
return Duration.standardDays(config.registryPolicy.contactAutomaticTransferDays);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of entities that can be checked at one time in an EPP check flow.
|
||||
*/
|
||||
@@ -1264,12 +1252,6 @@ public final class RegistryConfig {
|
||||
return ImmutableSet.copyOf(config.sslCertificateValidation.allowedEcdsaCurves);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("minMonthsBeforeWipeOut")
|
||||
public static int provideMinMonthsBeforeWipeOut(RegistryConfigSettings config) {
|
||||
return config.contactHistory.minMonthsBeforeWipeOut;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("jdbcBatchSize")
|
||||
public static int provideHibernateJdbcBatchSize(RegistryConfigSettings config) {
|
||||
@@ -1463,9 +1445,9 @@ public final class RegistryConfig {
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("mosapiTldThreadCnt")
|
||||
@Config("mosapiTldThreadCount")
|
||||
public static int provideMosapiTldThreads(RegistryConfigSettings config) {
|
||||
return config.mosapi.tldThreadCnt;
|
||||
return config.mosapi.tldThreadCount;
|
||||
}
|
||||
|
||||
private static String formatComments(String text) {
|
||||
@@ -1629,15 +1611,11 @@ public final class RegistryConfig {
|
||||
return CONFIG_SETTINGS.get().hibernate.jdbcFetchSize;
|
||||
}
|
||||
|
||||
/** Returns the roid suffix to be used for the roids of all contacts and hosts. */
|
||||
public static String getContactAndHostRoidSuffix() {
|
||||
/** Returns the roid suffix to be used for the roids of all hosts. */
|
||||
public static String getHostRoidSuffix() {
|
||||
return CONFIG_SETTINGS.get().registryPolicy.contactAndHostRoidSuffix;
|
||||
}
|
||||
|
||||
/** Returns the global automatic transfer length for contacts. */
|
||||
public static Duration getContactAutomaticTransferLength() {
|
||||
return Duration.standardDays(CONFIG_SETTINGS.get().registryPolicy.contactAutomaticTransferDays);
|
||||
}
|
||||
|
||||
/** A discount for all sunrise domain creates, between 0.0 (no discount) and 1.0 (free). */
|
||||
public static double getSunriseDomainCreateDiscount() {
|
||||
|
||||
@@ -39,7 +39,6 @@ public class RegistryConfigSettings {
|
||||
public Beam beam;
|
||||
public RegistryTool registryTool;
|
||||
public SslCertificateValidation sslCertificateValidation;
|
||||
public ContactHistory contactHistory;
|
||||
public DnsUpdate dnsUpdate;
|
||||
public BulkPricingPackageMonitoring bulkPricingPackageMonitoring;
|
||||
public Bsa bsa;
|
||||
@@ -87,7 +86,6 @@ public class RegistryConfigSettings {
|
||||
public String productName;
|
||||
public String customLogicFactoryClass;
|
||||
public String dnsCountQueryCoordinatorClass;
|
||||
public int contactAutomaticTransferDays;
|
||||
public String greetingServerId;
|
||||
public List<String> registrarChangesNotificationEmailAddresses;
|
||||
public String defaultRegistrarWhoisServer;
|
||||
@@ -223,11 +221,6 @@ public class RegistryConfigSettings {
|
||||
public String expirationWarningEmailSubjectText;
|
||||
}
|
||||
|
||||
/** Configuration for contact history. */
|
||||
public static class ContactHistory {
|
||||
public int minMonthsBeforeWipeOut;
|
||||
}
|
||||
|
||||
/** Configuration for dns update. */
|
||||
public static class DnsUpdate {
|
||||
public String dnsUpdateFailEmailSubjectText;
|
||||
@@ -272,6 +265,6 @@ public class RegistryConfigSettings {
|
||||
public String entityType;
|
||||
public List<String> tlds;
|
||||
public List<String> services;
|
||||
public int tldThreadCnt;
|
||||
public int tldThreadCount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,9 +63,6 @@ registryPolicy:
|
||||
# See reporting/icann/DnsCountQueryCoordinator.java
|
||||
dnsCountQueryCoordinatorClass: google.registry.reporting.icann.DummyDnsCountQueryCoordinator
|
||||
|
||||
# Length of time after which contact transfers automatically conclude.
|
||||
contactAutomaticTransferDays: 5
|
||||
|
||||
# Server ID used in the 'svID' element of an EPP 'greeting'.
|
||||
greetingServerId: Nomulus Registry
|
||||
|
||||
@@ -450,11 +447,6 @@ registryTool:
|
||||
# OAuth client secret used by the tool.
|
||||
clientSecret: YOUR_CLIENT_SECRET
|
||||
|
||||
# Configuration options for handling contact history.
|
||||
contactHistory:
|
||||
# The number of months that a ContactHistory entity should be stored in the database.
|
||||
minMonthsBeforeWipeOut: 18
|
||||
|
||||
# Configuration options relevant to the DNS update functionality.
|
||||
dnsUpdate:
|
||||
dnsUpdateFailRegistryName: Example name
|
||||
@@ -645,5 +637,5 @@ mosapi:
|
||||
# Provides a fixed thread pool for parallel TLD processing.
|
||||
# @see <a href="https://www.icann.org/mosapi-specification.pdf">
|
||||
# ICANN MoSAPI Specification, Section 12.3</a>
|
||||
tldThreadCnt: 4
|
||||
tldThreadCount: 4
|
||||
|
||||
|
||||
@@ -322,4 +322,15 @@
|
||||
<service>bsa</service>
|
||||
<schedule>23 8,20 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/triggerMosApiServiceState]]></url>
|
||||
<name>triggerMosApiServiceState</name>
|
||||
<description>
|
||||
Fetches the service state from MosAPI and triggers the metrics status for all TLDs.
|
||||
</description>
|
||||
<!-- Runs every 5 minutes. -->
|
||||
<schedule>*/5 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
</entries>
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
// Copyright 2026 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;
|
||||
|
||||
package google.registry.flows;
|
||||
|
||||
import static google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension.FEE_0_11;
|
||||
import static google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension.FEE_0_12;
|
||||
import static google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension.FEE_0_6;
|
||||
import static google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension.FEE_1_00;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.eppcommon.EppXmlTransformer;
|
||||
import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Normalizes Fee extension namespace tags in EPP XML response messages.
|
||||
*
|
||||
* <p>Nomulus currently supports multiple versions of the Fee extension. With the current XML
|
||||
* tooling, the namespace of every version is included in each EPP response, and as a result must
|
||||
* use a unique XML tag. E.g., fee for extension v0.6, and fee12 for extension v0.12.
|
||||
*
|
||||
* <p>Some registrars are not XML namespace-aware and rely on the XML tags being specific literals.
|
||||
* This makes it difficult to perform seamless rollout of new versions: if Nomulus reassigns a tag
|
||||
* literal to a different version, it effectively forces all these registrars to upgrade at the time
|
||||
* of the deployment.
|
||||
*
|
||||
* <p>This class can be used to normalize the namespace tag in EPP responses. Since every response
|
||||
* message may use at most one version of the Fee extension, we can remove declared but unused
|
||||
* versions from the message, thus freeing up the canonical tag ('fee') for the active version.
|
||||
*/
|
||||
public class FeeExtensionXmlTagNormalizer {
|
||||
|
||||
// So far we only have Fee extensions to process
|
||||
private static final String CANONICAL_FEE_TAG = "fee";
|
||||
|
||||
private static final ImmutableSet<ServiceExtension> FEE_EXTENSIONS =
|
||||
ImmutableSet.of(FEE_0_6, FEE_0_11, FEE_0_12, FEE_1_00);
|
||||
|
||||
private static final Pattern FEE_EXTENSION_IN_USE_PATTERN =
|
||||
Pattern.compile(feeExtensionInUseRegex());
|
||||
|
||||
@VisibleForTesting
|
||||
static String feeExtensionInUseRegex() {
|
||||
return FEE_EXTENSIONS.stream()
|
||||
.map(ServiceExtension::getXmlTag)
|
||||
.map(tag -> String.format("\\b(%s):", tag))
|
||||
.collect(Collectors.joining("|"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a EPP response that uses the canonical tag ({@code fee}) for the fee extension.
|
||||
*
|
||||
* <p>This method replaces any versioned tag, e.g., {@code fee12} with the canonical tag. It also
|
||||
* removes unused namespace declarations and update the tag in the remaining declaration.
|
||||
*
|
||||
* <p>The input {@code xml} must be an EPP response message generated by the {@link
|
||||
* EppXmlTransformer}. With this assumption, we can use regular expressions which is 10X faster
|
||||
* than XML stream parsers.
|
||||
*/
|
||||
public static String normalize(String xml) {
|
||||
Optional<String> maybeFeeTagInUse = findFeeExtensionInUse(xml);
|
||||
if (maybeFeeTagInUse.isEmpty()) {
|
||||
return xml;
|
||||
}
|
||||
String feeTagInUse = maybeFeeTagInUse.get();
|
||||
String normalized = xml;
|
||||
for (ServiceExtension serviceExtension : FEE_EXTENSIONS) {
|
||||
if (serviceExtension.getXmlTag().equals(feeTagInUse)) {
|
||||
normalized = normalizeExtensionInUse(feeTagInUse, serviceExtension.getUri(), normalized);
|
||||
} else {
|
||||
normalized =
|
||||
removeUnusedExtension(
|
||||
serviceExtension.getXmlTag(), serviceExtension.getUri(), normalized);
|
||||
}
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
static String removeUnusedExtension(String tag, String uri, String xml) {
|
||||
String declaration = String.format("xmlns:%s=\"%s\"", tag, uri);
|
||||
// There must be a leading whitespace, and it can be safely removed with the declaration.
|
||||
return xml.replaceAll(String.format("\\s%s", declaration), "");
|
||||
}
|
||||
|
||||
static String normalizeExtensionInUse(String tagInUse, String uriInUse, String xml) {
|
||||
if (tagInUse.equals(CANONICAL_FEE_TAG)) {
|
||||
return xml;
|
||||
}
|
||||
// Change the tag in the namespace declaration:
|
||||
String currentDeclaration = String.format("xmlns:%s=\"%s\"", tagInUse, uriInUse);
|
||||
String desiredDeclaraion = String.format("xmlns:fee=\"%s\"", uriInUse);
|
||||
// The new tag at each site of use, with trailing colon:
|
||||
String newTagWithColon = CANONICAL_FEE_TAG + ":";
|
||||
return xml.replaceAll(String.format("\\b%s:", tagInUse), newTagWithColon)
|
||||
.replaceAll(currentDeclaration, desiredDeclaraion);
|
||||
}
|
||||
|
||||
static Optional<String> findFeeExtensionInUse(String xml) {
|
||||
Matcher matcher = FEE_EXTENSION_IN_USE_PATTERN.matcher(xml);
|
||||
|
||||
if (!matcher.find()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
// We know only one extension is in use, so we can return on the first match
|
||||
for (int i = 1; i <= matcher.groupCount(); i++) {
|
||||
if (matcher.group(i) != null) {
|
||||
return Optional.of(matcher.group(i));
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Should not reach here. Bad FEE_EXTENSION_IN_USE_PATTERN?");
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import com.google.common.base.Strings;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.flows.picker.FlowPicker;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
@@ -267,23 +266,6 @@ public class FlowModule {
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a partially filled in {@link ContactHistory.Builder}
|
||||
*
|
||||
* <p>This is not marked with {@link FlowScope} so that each retry gets a fresh one. Otherwise,
|
||||
* the fact that the builder is one-use would cause NPEs.
|
||||
*/
|
||||
@Provides
|
||||
static ContactHistory.Builder provideContactHistoryBuilder(
|
||||
Trid trid,
|
||||
@InputXml byte[] inputXmlBytes,
|
||||
@Superuser boolean isSuperuser,
|
||||
@RegistrarId String registrarId,
|
||||
EppInput eppInput) {
|
||||
return makeHistoryEntryBuilder(
|
||||
new ContactHistory.Builder(), trid, inputXmlBytes, isSuperuser, registrarId, eppInput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a partially filled in {@link HostHistory.Builder}
|
||||
*
|
||||
|
||||
@@ -16,7 +16,6 @@ package google.registry.flows;
|
||||
|
||||
import static com.google.common.collect.Sets.intersection;
|
||||
import static google.registry.model.EppResourceUtils.isLinked;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
@@ -35,15 +34,14 @@ import google.registry.flows.exceptions.ResourceToDeleteIsReferencedException;
|
||||
import google.registry.flows.exceptions.TooManyResourceChecksException;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.EppResource.ForeignKeyedEppResource;
|
||||
import google.registry.model.EppResource.ResourceWithTransferData;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.List;
|
||||
@@ -65,30 +63,26 @@ public final class ResourceFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether if there are domains linked to the resource to be deleted. Throws an exception if
|
||||
* so.
|
||||
*/
|
||||
public static <R extends EppResource> void checkLinkedDomains(
|
||||
final String targetId, final DateTime now, final Class<R> resourceClass) throws EppException {
|
||||
VKey<R> key =
|
||||
ForeignKeyUtils.loadKey(resourceClass, targetId, now)
|
||||
.orElseThrow(() -> new ResourceDoesNotExistException(resourceClass, targetId));
|
||||
/** Check if there are domains linked to the host to be deleted. Throws an exception if so. */
|
||||
public static void checkLinkedDomains(final String targetId, final DateTime now)
|
||||
throws EppException {
|
||||
VKey<Host> key =
|
||||
ForeignKeyUtils.loadKey(Host.class, targetId, now)
|
||||
.orElseThrow(() -> new ResourceDoesNotExistException(Host.class, targetId));
|
||||
if (isLinked(key, now)) {
|
||||
throw new ResourceToDeleteIsReferencedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static <R extends EppResource & ResourceWithTransferData> void verifyHasPendingTransfer(
|
||||
R resource) throws NotPendingTransferException {
|
||||
if (resource.getTransferData().getTransferStatus() != TransferStatus.PENDING) {
|
||||
throw new NotPendingTransferException(resource.getForeignKey());
|
||||
public static void verifyHasPendingTransfer(Domain domain) throws NotPendingTransferException {
|
||||
if (domain.getTransferData().getTransferStatus() != TransferStatus.PENDING) {
|
||||
throw new NotPendingTransferException(domain.getForeignKey());
|
||||
}
|
||||
}
|
||||
|
||||
public static <R extends EppResource & ResourceWithTransferData> void verifyTransferInitiator(
|
||||
String registrarId, R resource) throws NotTransferInitiatorException {
|
||||
if (!resource.getTransferData().getGainingRegistrarId().equals(registrarId)) {
|
||||
public static void verifyTransferInitiator(String registrarId, Domain domain)
|
||||
throws NotTransferInitiatorException {
|
||||
if (!domain.getTransferData().getGainingRegistrarId().equals(registrarId)) {
|
||||
throw new NotTransferInitiatorException();
|
||||
}
|
||||
}
|
||||
@@ -124,14 +118,6 @@ public final class ResourceFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** Check that the given AuthInfo is either missing or else is valid for the given resource. */
|
||||
public static void verifyOptionalAuthInfo(Optional<AuthInfo> authInfo, Contact contact)
|
||||
throws EppException {
|
||||
if (authInfo.isPresent()) {
|
||||
verifyAuthInfo(authInfo.get(), contact);
|
||||
}
|
||||
}
|
||||
|
||||
/** Check that the given AuthInfo is either missing or else is valid for the given resource. */
|
||||
public static void verifyOptionalAuthInfo(Optional<AuthInfo> authInfo, Domain domain)
|
||||
throws EppException {
|
||||
@@ -142,37 +128,14 @@ public final class ResourceFlowUtils {
|
||||
|
||||
/** Check that the given {@link AuthInfo} is valid for the given domain. */
|
||||
public static void verifyAuthInfo(AuthInfo authInfo, Domain domain) throws EppException {
|
||||
final String authRepoId = authInfo.getPw().getRepoId();
|
||||
String authPassword = authInfo.getPw().getValue();
|
||||
if (authRepoId == null) {
|
||||
// If no roid is specified, check the password against the domain's password.
|
||||
String domainPassword = domain.getAuthInfo().getPw().getValue();
|
||||
if (!domainPassword.equals(authPassword)) {
|
||||
throw new BadAuthInfoForResourceException();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// The roid should match one of the contacts.
|
||||
Optional<VKey<Contact>> foundContact =
|
||||
domain.getReferencedContacts().stream()
|
||||
.filter(key -> key.getKey().equals(authRepoId))
|
||||
.findFirst();
|
||||
if (foundContact.isEmpty()) {
|
||||
String authRepoId = authInfo.getPw().getRepoId();
|
||||
// Previously one could auth against a contact, but we no longer hold any contact info
|
||||
if (authRepoId != null) {
|
||||
throw new BadAuthInfoForResourceException();
|
||||
}
|
||||
// Check the authInfo against the contact.
|
||||
verifyAuthInfo(authInfo, tm().loadByKey(foundContact.get()));
|
||||
}
|
||||
|
||||
/** Check that the given {@link AuthInfo} is valid for the given contact. */
|
||||
public static void verifyAuthInfo(AuthInfo authInfo, Contact contact) throws EppException {
|
||||
String authRepoId = authInfo.getPw().getRepoId();
|
||||
String authPassword = authInfo.getPw().getValue();
|
||||
String contactPassword = contact.getAuthInfo().getPw().getValue();
|
||||
if (!contactPassword.equals(authPassword)
|
||||
// It's unnecessary to specify a repoId on a contact auth info, but if it's there validate
|
||||
// it. The usual case of this is validating a domain's auth using this method.
|
||||
|| (authRepoId != null && !authRepoId.equals(contact.getRepoId()))) {
|
||||
String domainPassword = domain.getAuthInfo().getPw().getValue();
|
||||
if (!domainPassword.equals(authPassword)) {
|
||||
throw new BadAuthInfoForResourceException();
|
||||
}
|
||||
}
|
||||
@@ -194,27 +157,24 @@ public final class ResourceFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** Check that the same values aren't being added and removed in an update command. */
|
||||
public static <T> void checkSameValuesNotAddedAndRemoved(
|
||||
ImmutableSet<T> fieldsToAdd, ImmutableSet<T> fieldsToRemove)
|
||||
throws AddRemoveSameValueException {
|
||||
/**
|
||||
* Verifies the adds and removes on a resource.
|
||||
*
|
||||
* <p>This throws an exception in three different situations: if the same value is being both
|
||||
* added and removed, if a value is being added that is already present, or if a value is being
|
||||
* removed that isn't present.
|
||||
*/
|
||||
public static <T> void verifyAddsAndRemoves(
|
||||
ImmutableSet<T> existingFields, ImmutableSet<T> fieldsToAdd, ImmutableSet<T> fieldsToRemove)
|
||||
throws AddRemoveSameValueException,
|
||||
AddExistingValueException,
|
||||
RemoveNonexistentValueException {
|
||||
if (!intersection(fieldsToAdd, fieldsToRemove).isEmpty()) {
|
||||
throw new AddRemoveSameValueException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Check that we aren't adding a value that is already present. */
|
||||
public static <T> void checkExistingValueNotAdded(
|
||||
ImmutableSet<T> fieldsToAdd, ImmutableSet<T> existingFields)
|
||||
throws AddExistingValueException {
|
||||
if (!intersection(fieldsToAdd, existingFields).isEmpty()) {
|
||||
throw new AddExistingValueException();
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> void checkNonexistentValueNotRemoved(
|
||||
ImmutableSet<T> fieldsToRemove, ImmutableSet<T> existingFields)
|
||||
throws RemoveNonexistentValueException {
|
||||
if (intersection(fieldsToRemove, existingFields).size() != fieldsToRemove.size()) {
|
||||
throw new RemoveNonexistentValueException();
|
||||
}
|
||||
|
||||
@@ -167,7 +167,6 @@ import org.joda.time.Duration;
|
||||
* @error {@link DomainFlowUtils.DomainLabelBlockedByBsaException}
|
||||
* @error {@link DomainFlowUtils.DomainLabelTooLongException}
|
||||
* @error {@link DomainFlowUtils.DomainReservedException}
|
||||
* @error {@link DomainFlowUtils.DuplicateContactForRoleException}
|
||||
* @error {@link DomainFlowUtils.EmptyDomainNamePartException}
|
||||
* @error {@link DomainFlowUtils.ExceedsMaxRegistrationYearsException}
|
||||
* @error {@link DomainFlowUtils.ExpiredClaimException}
|
||||
@@ -188,7 +187,6 @@ import org.joda.time.Duration;
|
||||
* @error {@link DomainFlowUtils.MaxSigLifeNotSupportedException}
|
||||
* @error {@link DomainFlowUtils.MissingBillingAccountMapException}
|
||||
* @error {@link DomainFlowUtils.MissingClaimsNoticeException}
|
||||
* @error {@link DomainFlowUtils.MissingContactTypeException}
|
||||
* @error {@link DomainFlowUtils.NameserversNotAllowedForTldException}
|
||||
* @error {@link DomainFlowUtils.NameserversNotSpecifiedForTldWithNameserverAllowListException}
|
||||
* @error {@link DomainFlowUtils.PremiumNameBlockedException}
|
||||
@@ -221,7 +219,8 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@Inject DomainDeletionTimeCache domainDeletionTimeCache;
|
||||
|
||||
@Inject DomainCreateFlow() {}
|
||||
@Inject
|
||||
DomainCreateFlow() {}
|
||||
|
||||
@Override
|
||||
public EppResponse run() throws EppException {
|
||||
@@ -378,12 +377,10 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
.setLaunchNotice(hasClaimsNotice ? launchCreate.get().getNotice() : null)
|
||||
.setSmdId(signedMarkId)
|
||||
.setDsData(secDnsCreate.map(SecDnsCreateExtension::getDsData).orElse(null))
|
||||
.setRegistrant(command.getRegistrant())
|
||||
.setAuthInfo(command.getAuthInfo())
|
||||
.setDomainName(targetId)
|
||||
.setNameservers(command.getNameservers().stream().collect(toImmutableSet()))
|
||||
.setStatusValues(statuses)
|
||||
.setContacts(command.getContacts())
|
||||
.addGracePeriod(
|
||||
GracePeriod.forBillingEvent(GracePeriodStatus.ADD, repoId, createBillingEvent))
|
||||
.setLordnPhase(
|
||||
|
||||
@@ -157,7 +157,8 @@ public final class DomainFlowTmchUtils {
|
||||
}
|
||||
|
||||
/** The provided mark does not match the desired domain label. */
|
||||
static class NoMarksFoundMatchingDomainException extends RequiredParameterMissingException {
|
||||
public static class NoMarksFoundMatchingDomainException
|
||||
extends RequiredParameterMissingException {
|
||||
public NoMarksFoundMatchingDomainException() {
|
||||
super("The provided mark does not match the desired domain label");
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static com.google.common.collect.Sets.intersection;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
@@ -45,10 +44,8 @@ import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
|
||||
import static google.registry.util.DomainNameUtils.ACE_PREFIX;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
@@ -57,9 +54,6 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
@@ -81,14 +75,12 @@ import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingRecurrence;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.DesignatedContact.Type;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainCommand.Create;
|
||||
import google.registry.model.domain.DomainCommand.CreateOrUpdate;
|
||||
import google.registry.model.domain.DomainCommand.InvalidReferencesException;
|
||||
import google.registry.model.domain.DomainCommand.Update;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.ForeignKeyedDesignatedContact;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.Period.Unit;
|
||||
import google.registry.model.domain.fee.BaseFee;
|
||||
@@ -133,10 +125,8 @@ import google.registry.tldconfig.idn.IdnLabelValidator;
|
||||
import google.registry.tools.DigestType;
|
||||
import google.registry.util.Idn;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -405,22 +395,11 @@ public class DomainFlowUtils {
|
||||
return period;
|
||||
}
|
||||
|
||||
/** Verify that no linked resources have disallowed statuses. */
|
||||
static void verifyNotInPendingDelete(
|
||||
Set<DesignatedContact> contacts,
|
||||
Optional<VKey<Contact>> registrant,
|
||||
Set<VKey<Host>> nameservers)
|
||||
throws EppException {
|
||||
ImmutableList.Builder<VKey<? extends EppResource>> keysToLoad = new ImmutableList.Builder<>();
|
||||
contacts.stream().map(DesignatedContact::getContactKey).forEach(keysToLoad::add);
|
||||
registrant.ifPresent(keysToLoad::add);
|
||||
keysToLoad.addAll(nameservers);
|
||||
verifyNotInPendingDelete(EppResource.loadByCacheIfEnabled(keysToLoad.build()).values());
|
||||
}
|
||||
|
||||
private static void verifyNotInPendingDelete(Iterable<EppResource> resources)
|
||||
throws EppException {
|
||||
for (EppResource resource : resources) {
|
||||
/** Verify that no linked nameservers have disallowed statuses. */
|
||||
static void verifyNotInPendingDelete(ImmutableSet<VKey<Host>> nameservers)
|
||||
throws StatusProhibitsOperationException {
|
||||
for (EppResource resource :
|
||||
EppResource.loadByCacheIfEnabled(ImmutableSet.copyOf(nameservers)).values()) {
|
||||
if (resource.getStatusValues().contains(StatusValue.PENDING_DELETE)) {
|
||||
throw new LinkedResourceInPendingDeleteProhibitsOperationException(
|
||||
resource.getForeignKey());
|
||||
@@ -428,15 +407,6 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static void validateContactsHaveTypes(Set<DesignatedContact> contacts)
|
||||
throws ParameterValuePolicyErrorException {
|
||||
for (DesignatedContact contact : contacts) {
|
||||
if (contact.getType() == null) {
|
||||
throw new MissingContactTypeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void validateNameserversCountForTld(String tld, InternetDomainName domainName, int count)
|
||||
throws EppException {
|
||||
// For TLDs with a nameserver allow list, all domains must have at least 1 nameserver.
|
||||
@@ -451,36 +421,22 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static void validateNoDuplicateContacts(Set<DesignatedContact> contacts)
|
||||
/** Enforces absence of contact data on creation as part of the Minimum Dataset requirements. */
|
||||
static void enforceContactAbsencesOnCreate(Create create)
|
||||
throws ParameterValuePolicyErrorException {
|
||||
ImmutableMultimap<Type, VKey<Contact>> contactsByType =
|
||||
contacts.stream()
|
||||
.collect(
|
||||
toImmutableSetMultimap(
|
||||
DesignatedContact::getType, DesignatedContact::getContactKey));
|
||||
|
||||
// If any contact type has multiple contacts:
|
||||
if (contactsByType.asMap().values().stream().anyMatch(v -> v.size() > 1)) {
|
||||
// Find the duplicates.
|
||||
Map<Type, Collection<VKey<Contact>>> dupeKeysMap =
|
||||
Maps.filterEntries(contactsByType.asMap(), e -> e.getValue().size() > 1);
|
||||
ImmutableList<VKey<Contact>> dupeKeys =
|
||||
dupeKeysMap.values().stream().flatMap(Collection::stream).collect(toImmutableList());
|
||||
// Load the duplicates in one batch.
|
||||
Map<VKey<? extends Contact>, Contact> dupeContacts = tm().loadByKeys(dupeKeys);
|
||||
ImmutableMultimap.Builder<Type, VKey<Contact>> typesMap = new ImmutableMultimap.Builder<>();
|
||||
dupeKeysMap.forEach(typesMap::putAll);
|
||||
// Create an error message showing the type and contact IDs of the duplicates.
|
||||
throw new DuplicateContactForRoleException(
|
||||
Multimaps.transformValues(typesMap.build(), key -> dupeContacts.get(key).getContactId()));
|
||||
}
|
||||
enforceContactAbsences(create.getRegistrant(), create.getContacts());
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforces the presence/absence of contact data on domain creates depending on the minimum data
|
||||
* set migration schedule.
|
||||
*/
|
||||
static void validateCreateContactData(
|
||||
/** Enforces absence of contact data on update as part of the Minimum Dataset requirements. */
|
||||
static void enforceContactAbsencesOnUpdate(Update update)
|
||||
throws ParameterValuePolicyErrorException {
|
||||
Set<DesignatedContact> allDesignatedContacts =
|
||||
Sets.union(update.getInnerAdd().getContacts(), update.getInnerRemove().getContacts());
|
||||
enforceContactAbsences(update.getInnerChange().getRegistrant(), allDesignatedContacts);
|
||||
}
|
||||
|
||||
/** Enforces the absence of contact data as part of the Minimum Dataset requirements. */
|
||||
static void enforceContactAbsences(
|
||||
Optional<VKey<Contact>> registrant, Set<DesignatedContact> contacts)
|
||||
throws ParameterValuePolicyErrorException {
|
||||
if (registrant.isPresent()) {
|
||||
@@ -491,25 +447,6 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforces the presence/absence of contact data on domain updates depending on the minimum data
|
||||
* set migration schedule.
|
||||
*/
|
||||
static void validateUpdateContactData(
|
||||
Optional<VKey<Contact>> existingRegistrant,
|
||||
Optional<VKey<Contact>> newRegistrant,
|
||||
Set<DesignatedContact> existingContacts,
|
||||
Set<DesignatedContact> newContacts)
|
||||
throws ParameterValuePolicyErrorException {
|
||||
// Throw if the update specifies a new registrant that is different from the existing one.
|
||||
if (newRegistrant.isPresent() && !newRegistrant.equals(existingRegistrant)) {
|
||||
throw new RegistrantProhibitedException();
|
||||
}
|
||||
// Throw if the update specifies any new contacts that weren't already present on the domain.
|
||||
if (!Sets.difference(newContacts, existingContacts).isEmpty()) {
|
||||
throw new ContactsProhibitedException();
|
||||
}
|
||||
}
|
||||
|
||||
static void validateNameserversAllowedOnTld(String tld, Set<String> fullyQualifiedHostNames)
|
||||
throws EppException {
|
||||
@@ -976,23 +913,21 @@ public class DomainFlowUtils {
|
||||
throw new UrgentAttributeNotSupportedException();
|
||||
}
|
||||
// There must be at least one of add/rem/chg, and chg isn't actually supported.
|
||||
if (secDnsUpdate.getChange() != null) {
|
||||
if (secDnsUpdate.getChange().isPresent()) {
|
||||
// The only thing you can change is maxSigLife, and we don't support that at all.
|
||||
throw new MaxSigLifeChangeNotSupportedException();
|
||||
}
|
||||
Add add = secDnsUpdate.getAdd();
|
||||
Remove remove = secDnsUpdate.getRemove();
|
||||
if (add == null && remove == null) {
|
||||
Optional<Add> add = secDnsUpdate.getAdd();
|
||||
Optional<Remove> remove = secDnsUpdate.getRemove();
|
||||
if (add.isEmpty() && remove.isEmpty()) {
|
||||
throw new EmptySecDnsUpdateException();
|
||||
}
|
||||
if (remove != null && Boolean.FALSE.equals(remove.getAll())) {
|
||||
if (remove.isPresent() && Boolean.FALSE.equals(remove.get().getAll())) {
|
||||
throw new SecDnsAllUsageException(); // Explicit all=false is meaningless.
|
||||
}
|
||||
Set<DomainDsData> toAdd = (add == null) ? ImmutableSet.of() : add.getDsData();
|
||||
Set<DomainDsData> toAdd = add.map(Add::getDsData).orElse(ImmutableSet.of());
|
||||
Set<DomainDsData> toRemove =
|
||||
(remove == null)
|
||||
? ImmutableSet.of()
|
||||
: (remove.getAll() == null) ? remove.getDsData() : oldDsData;
|
||||
remove.map(r -> (r.getAll() == null) ? r.getDsData() : oldDsData).orElse(ImmutableSet.of());
|
||||
// RFC 5910 specifies that removes are processed before adds.
|
||||
return ImmutableSet.copyOf(union(difference(oldDsData, toRemove), toAdd));
|
||||
}
|
||||
@@ -1034,12 +969,9 @@ public class DomainFlowUtils {
|
||||
/** Validate the contacts and nameservers specified in a domain create command. */
|
||||
static void validateCreateCommandContactsAndNameservers(
|
||||
Create command, Tld tld, InternetDomainName domainName) throws EppException {
|
||||
verifyNotInPendingDelete(
|
||||
command.getContacts(), command.getRegistrant(), command.getNameservers());
|
||||
validateContactsHaveTypes(command.getContacts());
|
||||
verifyNotInPendingDelete(command.getNameservers());
|
||||
String tldStr = tld.getTldStr();
|
||||
validateNoDuplicateContacts(command.getContacts());
|
||||
validateCreateContactData(command.getRegistrant(), command.getContacts());
|
||||
enforceContactAbsencesOnCreate(command);
|
||||
ImmutableSet<String> hostNames = command.getNameserverHostNames();
|
||||
validateNameserversCountForTld(tldStr, domainName, hostNames.size());
|
||||
validateNameserversAllowedOnTld(tldStr, hostNames);
|
||||
@@ -1145,17 +1077,6 @@ public class DomainFlowUtils {
|
||||
.build();
|
||||
}
|
||||
|
||||
static ImmutableSet<ForeignKeyedDesignatedContact> loadForeignKeyedDesignatedContacts(
|
||||
ImmutableSet<DesignatedContact> contacts) {
|
||||
ImmutableSet.Builder<ForeignKeyedDesignatedContact> builder = new ImmutableSet.Builder<>();
|
||||
for (DesignatedContact contact : contacts) {
|
||||
builder.add(
|
||||
ForeignKeyedDesignatedContact.create(
|
||||
contact.getType(), tm().loadByKey(contact.getContactKey()).getContactId()));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of DomainTransactionRecords which negate the most recent HistoryEntry's records.
|
||||
*
|
||||
@@ -1295,32 +1216,6 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** Missing type attribute for contact. */
|
||||
static class MissingContactTypeException extends ParameterValuePolicyErrorException {
|
||||
public MissingContactTypeException() {
|
||||
super("Missing type attribute for contact");
|
||||
}
|
||||
}
|
||||
|
||||
/** More than one contact for a given role is not allowed. */
|
||||
static class DuplicateContactForRoleException extends ParameterValuePolicyErrorException {
|
||||
|
||||
public DuplicateContactForRoleException(Multimap<Type, String> dupeContactsByType) {
|
||||
super(
|
||||
String.format(
|
||||
"More than one contact for a given role is not allowed: %s",
|
||||
dupeContactsByType.asMap().entrySet().stream()
|
||||
.sorted(comparing(e -> e.getKey().name()))
|
||||
.map(
|
||||
e ->
|
||||
String.format(
|
||||
"role [%s] has contacts [%s]",
|
||||
Ascii.toLowerCase(e.getKey().name()),
|
||||
e.getValue().stream().sorted().collect(joining(", "))))
|
||||
.collect(joining(", "))));
|
||||
}
|
||||
}
|
||||
|
||||
/** Declared launch extension phase does not match the current registry phase. */
|
||||
static class LaunchPhaseMismatchException extends ParameterValuePolicyErrorException {
|
||||
public LaunchPhaseMismatchException() {
|
||||
@@ -1357,7 +1252,7 @@ public class DomainFlowUtils {
|
||||
}
|
||||
|
||||
/** Having a registrant is prohibited by registry policy. */
|
||||
static class RegistrantProhibitedException extends ParameterValuePolicyErrorException {
|
||||
public static class RegistrantProhibitedException extends ParameterValuePolicyErrorException {
|
||||
public RegistrantProhibitedException() {
|
||||
super("Having a registrant is prohibited by registry policy");
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.addSecDnsExtensionIfPresent;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.loadForeignKeyedDesignatedContacts;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -126,15 +125,11 @@ public final class DomainInfoFlow implements MutatingFlow {
|
||||
.setLastEppUpdateTime(domain.getLastEppUpdateTime())
|
||||
.setRegistrationExpirationTime(domain.getRegistrationExpirationTime())
|
||||
.setLastTransferTime(domain.getLastTransferTime());
|
||||
domain
|
||||
.getRegistrant()
|
||||
.ifPresent(r -> infoBuilder.setRegistrant(tm().loadByKey(r).getContactId()));
|
||||
|
||||
// If authInfo is non-null, then the caller is authorized to see the full information since we
|
||||
// will have already verified the authInfo is valid.
|
||||
if (registrarId.equals(domain.getCurrentSponsorRegistrarId()) || authInfo.isPresent()) {
|
||||
infoBuilder
|
||||
.setContacts(loadForeignKeyedDesignatedContacts(domain.getContacts()))
|
||||
.setSubordinateHosts(
|
||||
hostsRequest.requestSubordinate() ? domain.getSubordinateHosts() : null)
|
||||
.setCreationRegistrarId(domain.getCreationRegistrarId())
|
||||
|
||||
@@ -21,24 +21,20 @@ import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.flows.FlowUtils.persistEntityChanges;
|
||||
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.checkExistingValueNotAdded;
|
||||
import static google.registry.flows.ResourceFlowUtils.checkNonexistentValueNotRemoved;
|
||||
import static google.registry.flows.ResourceFlowUtils.checkSameValuesNotAddedAndRemoved;
|
||||
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyAddsAndRemoves;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyAllStatusesAreClientSettable;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.enforceContactAbsencesOnUpdate;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.updateDsData;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateDsData;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateFeesAckedIfPresent;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAllowedOnTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCountForTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateUpdateContactData;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyClientUpdateNotProhibited;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_UPDATE;
|
||||
@@ -66,8 +62,6 @@ import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedExceptio
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainCommand.Update;
|
||||
import google.registry.model.domain.DomainCommand.Update.AddRemove;
|
||||
@@ -77,6 +71,8 @@ import google.registry.model.domain.fee.FeeUpdateCommandExtension;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.domain.secdns.DomainDsData;
|
||||
import google.registry.model.domain.secdns.SecDnsUpdateExtension;
|
||||
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Add;
|
||||
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Remove;
|
||||
import google.registry.model.domain.superuser.DomainUpdateSuperuserExtension;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
@@ -88,7 +84,6 @@ import google.registry.model.poll.PendingActionNotificationResponse.DomainPendin
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.persistence.VKey;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@@ -97,8 +92,8 @@ import org.joda.time.DateTime;
|
||||
/**
|
||||
* An EPP flow that updates a domain.
|
||||
*
|
||||
* <p>Updates can change contacts, nameservers and delegation signer data of a domain. Updates
|
||||
* cannot change the domain's name.
|
||||
* <p>Updates can change nameservers and delegation signer data of a domain. Updates cannot change
|
||||
* the domain's name.
|
||||
*
|
||||
* <p>Some status values (those of the form "serverSomethingProhibited") can only be applied by the
|
||||
* superuser. As such, adding or removing these statuses incurs a billing event. There will be only
|
||||
@@ -113,7 +108,6 @@ import org.joda.time.DateTime;
|
||||
* @error {@link google.registry.flows.exceptions.OnlyToolCanPassMetadataException}
|
||||
* @error {@link google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException}
|
||||
* @error {@link google.registry.flows.exceptions.ResourceStatusProhibitsOperationException}
|
||||
* @error {@link DomainFlowUtils.DuplicateContactForRoleException}
|
||||
* @error {@link DomainFlowUtils.EmptySecDnsUpdateException}
|
||||
* @error {@link DomainFlowUtils.FeesMismatchException}
|
||||
* @error {@link DomainFlowUtils.FeesRequiredForNonFreeOperationException}
|
||||
@@ -121,7 +115,6 @@ import org.joda.time.DateTime;
|
||||
* @error {@link DomainFlowUtils.LinkedResourcesDoNotExistException}
|
||||
* @error {@link DomainFlowUtils.LinkedResourceInPendingDeleteProhibitsOperationException}
|
||||
* @error {@link DomainFlowUtils.MaxSigLifeChangeNotSupportedException}
|
||||
* @error {@link DomainFlowUtils.MissingContactTypeException}
|
||||
* @error {@link DomainFlowUtils.NameserversNotAllowedForTldException}
|
||||
* @error {@link NameserversNotSpecifiedForTldWithNameserverAllowListException}
|
||||
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
|
||||
@@ -158,7 +151,9 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject DomainUpdateFlowCustomLogic flowCustomLogic;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@Inject DomainUpdateFlow() {}
|
||||
|
||||
@Inject
|
||||
DomainUpdateFlow() {}
|
||||
|
||||
@Override
|
||||
public EppResponse run() throws EppException {
|
||||
@@ -179,7 +174,7 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
Domain newDomain = performUpdate(command, existingDomain, now);
|
||||
DomainHistory domainHistory =
|
||||
historyBuilder.setType(DOMAIN_UPDATE).setDomain(newDomain).build();
|
||||
validateNewState(existingDomain, newDomain);
|
||||
validateNewState(newDomain);
|
||||
if (requiresDnsUpdate(existingDomain, newDomain)) {
|
||||
requestDomainDnsRefresh(targetId);
|
||||
}
|
||||
@@ -235,32 +230,26 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
eppInput.getSingleExtension(FeeUpdateCommandExtension.class);
|
||||
FeesAndCredits feesAndCredits = pricingLogic.getUpdatePrice(tld, targetId, now);
|
||||
validateFeesAckedIfPresent(feeUpdate, feesAndCredits, false);
|
||||
verifyNotInPendingDelete(
|
||||
add.getContacts(),
|
||||
command.getInnerChange().getRegistrant(),
|
||||
add.getNameservers());
|
||||
validateContactsHaveTypes(add.getContacts());
|
||||
validateContactsHaveTypes(remove.getContacts());
|
||||
verifyNotInPendingDelete(add.getNameservers());
|
||||
validateNameserversAllowedOnTld(tldStr, add.getNameserverHostNames());
|
||||
}
|
||||
|
||||
private Domain performUpdate(Update command, Domain domain, DateTime now) throws EppException {
|
||||
AddRemove add = command.getInnerAdd();
|
||||
AddRemove remove = command.getInnerRemove();
|
||||
checkSameValuesNotAddedAndRemoved(add.getNameservers(), remove.getNameservers());
|
||||
checkSameValuesNotAddedAndRemoved(add.getContacts(), remove.getContacts());
|
||||
checkSameValuesNotAddedAndRemoved(add.getStatusValues(), remove.getStatusValues());
|
||||
checkExistingValueNotAdded(add.getNameservers(), domain.getNameservers());
|
||||
checkNonexistentValueNotRemoved(remove.getNameservers(), domain.getNameservers());
|
||||
Change change = command.getInnerChange();
|
||||
Optional<SecDnsUpdateExtension> secDnsUpdate =
|
||||
eppInput.getSingleExtension(SecDnsUpdateExtension.class);
|
||||
|
||||
// We have to verify no duplicate contacts _before_ constructing the domain because it is
|
||||
// illegal to construct a domain with duplicate contacts.
|
||||
Sets.SetView<DesignatedContact> newContacts =
|
||||
union(Sets.difference(domain.getContacts(), remove.getContacts()), add.getContacts());
|
||||
validateNoDuplicateContacts(newContacts);
|
||||
verifyAddsAndRemoves(domain.getNameservers(), add.getNameservers(), remove.getNameservers());
|
||||
verifyAddsAndRemoves(domain.getStatusValues(), add.getStatusValues(), remove.getStatusValues());
|
||||
if (secDnsUpdate.isPresent()) {
|
||||
SecDnsUpdateExtension ext = secDnsUpdate.get();
|
||||
verifyAddsAndRemoves(
|
||||
domain.getDsData(),
|
||||
ext.getAdd().map(Add::getDsData).orElse(ImmutableSet.of()),
|
||||
ext.getRemove().map(Remove::getDsData).orElse(ImmutableSet.of()));
|
||||
}
|
||||
Change change = command.getInnerChange();
|
||||
enforceContactAbsencesOnUpdate(command);
|
||||
|
||||
Domain.Builder domainBuilder =
|
||||
domain
|
||||
@@ -280,9 +269,6 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
.setLastEppUpdateRegistrarId(registrarId)
|
||||
.addStatusValues(add.getStatusValues())
|
||||
.removeStatusValues(remove.getStatusValues())
|
||||
.removeContacts(remove.getContacts())
|
||||
.addContacts(add.getContacts())
|
||||
.setRegistrant(determineUpdatedRegistrant(change, domain))
|
||||
.setAuthInfo(Optional.ofNullable(change.getAuthInfo()).orElse(domain.getAuthInfo()));
|
||||
|
||||
if (!add.getNameservers().isEmpty()) {
|
||||
@@ -304,15 +290,6 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
return domainBuilder.build();
|
||||
}
|
||||
|
||||
private Optional<VKey<Contact>> determineUpdatedRegistrant(Change change, Domain domain) {
|
||||
// During or after the minimum dataset transition, allow registrant to be removed.
|
||||
if (change.getRegistrantContactId().isPresent()
|
||||
&& change.getRegistrantContactId().get().isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return change.getRegistrant().or(domain::getRegistrant);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the new state of the domain is valid.
|
||||
*
|
||||
@@ -320,13 +297,7 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
* compliant with the additions or amendments, otherwise existing data can become invalid and
|
||||
* cause Domain update failure.
|
||||
*/
|
||||
private static void validateNewState(Domain existingDomain, Domain newDomain)
|
||||
throws EppException {
|
||||
validateUpdateContactData(
|
||||
existingDomain.getRegistrant(),
|
||||
newDomain.getRegistrant(),
|
||||
existingDomain.getContacts(),
|
||||
newDomain.getContacts());
|
||||
private static void validateNewState(Domain newDomain) throws EppException {
|
||||
validateDsData(newDomain.getDsData());
|
||||
validateNameserversCountForTld(
|
||||
newDomain.getTld(),
|
||||
@@ -340,8 +311,8 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
Optional<MetadataExtension> metadataExtension =
|
||||
eppInput.getSingleExtension(MetadataExtension.class);
|
||||
if (metadataExtension.isPresent() && metadataExtension.get().getRequestedByRegistrar()) {
|
||||
for (StatusValue statusValue
|
||||
: symmetricDifference(existingDomain.getStatusValues(), newDomain.getStatusValues())) {
|
||||
for (StatusValue statusValue :
|
||||
symmetricDifference(existingDomain.getStatusValues(), newDomain.getStatusValues())) {
|
||||
if (statusValue.isChargedStatus()) {
|
||||
// Only charge once.
|
||||
return Optional.of(
|
||||
|
||||
@@ -87,7 +87,7 @@ public final class HostCreateFlow implements MutatingFlow {
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
|
||||
@Inject
|
||||
@Config("contactAndHostRoidSuffix")
|
||||
@Config("hostRoidSuffix")
|
||||
String roidSuffix;
|
||||
|
||||
@Inject
|
||||
|
||||
@@ -84,7 +84,7 @@ public final class HostDeleteFlow implements MutatingFlow {
|
||||
extensionManager.validate();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
validateHostName(targetId);
|
||||
checkLinkedDomains(targetId, now, Host.class);
|
||||
checkLinkedDomains(targetId, now);
|
||||
Host existingHost = loadAndVerifyExistence(Host.class, targetId, now);
|
||||
verifyNoDisallowedStatuses(existingHost, ImmutableSet.of(StatusValue.PENDING_DELETE));
|
||||
if (!isSuperuser) {
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.flows.host;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateFirstLabel;
|
||||
import static google.registry.model.EppResourceUtils.isActive;
|
||||
import static google.registry.model.tld.Tlds.findTldForName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
@@ -34,7 +35,6 @@ import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.util.Idn;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -57,7 +57,7 @@ public class HostFlowUtils {
|
||||
throw new HostNameNotLowerCaseException(hostNameLowerCase);
|
||||
}
|
||||
try {
|
||||
String hostNamePunyCoded = Idn.toASCII(name);
|
||||
String hostNamePunyCoded = canonicalizeHostname(name);
|
||||
if (!name.equals(hostNamePunyCoded)) {
|
||||
throw new HostNameNotPunyCodedException(hostNamePunyCoded);
|
||||
}
|
||||
|
||||
@@ -20,10 +20,8 @@ import static google.registry.dns.DnsUtils.requestHostDnsRefresh;
|
||||
import static google.registry.dns.RefreshDnsOnHostRenameAction.PARAM_HOST_KEY;
|
||||
import static google.registry.dns.RefreshDnsOnHostRenameAction.QUEUE_HOST_RENAME;
|
||||
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.checkExistingValueNotAdded;
|
||||
import static google.registry.flows.ResourceFlowUtils.checkNonexistentValueNotRemoved;
|
||||
import static google.registry.flows.ResourceFlowUtils.checkSameValuesNotAddedAndRemoved;
|
||||
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyAddsAndRemoves;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyAllStatusesAreClientSettable;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||
@@ -161,10 +159,10 @@ public final class HostUpdateFlow implements MutatingFlow {
|
||||
}
|
||||
AddRemove add = command.getInnerAdd();
|
||||
AddRemove remove = command.getInnerRemove();
|
||||
checkSameValuesNotAddedAndRemoved(add.getStatusValues(), remove.getStatusValues());
|
||||
checkSameValuesNotAddedAndRemoved(add.getInetAddresses(), remove.getInetAddresses());
|
||||
checkExistingValueNotAdded(add.getInetAddresses(), existingHost.getInetAddresses());
|
||||
checkNonexistentValueNotRemoved(remove.getInetAddresses(), existingHost.getInetAddresses());
|
||||
verifyAddsAndRemoves(
|
||||
existingHost.getStatusValues(), add.getStatusValues(), remove.getStatusValues());
|
||||
verifyAddsAndRemoves(
|
||||
existingHost.getInetAddresses(), add.getInetAddresses(), remove.getInetAddresses());
|
||||
HostFlowUtils.validateInetAddresses(add.getInetAddresses());
|
||||
VKey<Domain> newSuperordinateDomainKey =
|
||||
newSuperordinateDomain.map(Domain::createVKey).orElse(null);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.flows.session;
|
||||
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.PROHIBIT_CONTACT_OBJECTS_ON_LOGIN;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
|
||||
@@ -39,6 +40,7 @@ import google.registry.flows.TlsCredentials.BadRegistrarIpAddressException;
|
||||
import google.registry.flows.TlsCredentials.MissingRegistrarCertificateException;
|
||||
import google.registry.flows.TransportCredentials;
|
||||
import google.registry.flows.TransportCredentials.BadRegistrarPasswordException;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.eppcommon.ProtocolDefinition;
|
||||
import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension;
|
||||
import google.registry.model.eppinput.EppInput;
|
||||
@@ -114,9 +116,13 @@ public class LoginFlow implements MutatingFlow {
|
||||
}
|
||||
Services services = login.getServices();
|
||||
stopwatch.tick("LoginFlow getServices");
|
||||
Set<String> unsupportedObjectServices = difference(
|
||||
nullToEmpty(services.getObjectServices()),
|
||||
ProtocolDefinition.SUPPORTED_OBJECT_SERVICES);
|
||||
|
||||
Set<String> unsupportedObjectServices =
|
||||
difference(
|
||||
nullToEmpty(services.getObjectServices()),
|
||||
FeatureFlag.isActiveNow(PROHIBIT_CONTACT_OBJECTS_ON_LOGIN)
|
||||
? ProtocolDefinition.SUPPORTED_OBJECT_SERVICES
|
||||
: ProtocolDefinition.SUPPORTED_OBJECT_SERVICES_WITH_CONTACT);
|
||||
stopwatch.tick("LoginFlow difference unsupportedObjectServices");
|
||||
if (!unsupportedObjectServices.isEmpty()) {
|
||||
throw new UnimplementedObjectServiceException();
|
||||
|
||||
@@ -23,17 +23,14 @@ import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.EppResource.BuilderWithTransferData;
|
||||
import google.registry.model.EppResource.ResourceWithTransferData;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.HistoryEntryDao;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.transfer.DomainTransferData;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.persistence.VKey;
|
||||
import jakarta.persistence.Query;
|
||||
@@ -48,14 +45,6 @@ public final class EppResourceUtils {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final String CONTACT_LINKED_DOMAIN_QUERY =
|
||||
"SELECT repoId FROM Domain "
|
||||
+ "WHERE (adminContact = :fkRepoId "
|
||||
+ "OR billingContact = :fkRepoId "
|
||||
+ "OR techContact = :fkRepoId "
|
||||
+ "OR registrantContact = :fkRepoId) "
|
||||
+ "AND deletionTime > :now";
|
||||
|
||||
// We have to use the native SQL query here because DomainHost table doesn't have its entity
|
||||
// class, so we cannot reference its property like domainHost.hostRepoId in a JPQL query.
|
||||
private static final String HOST_LINKED_DOMAIN_QUERY =
|
||||
@@ -105,41 +94,34 @@ public final class EppResourceUtils {
|
||||
return !isActive(resource, time);
|
||||
}
|
||||
|
||||
/** Process an automatic transfer on a resource. */
|
||||
public static <
|
||||
T extends TransferData,
|
||||
B extends EppResource.Builder<?, B> & BuilderWithTransferData<T, B>>
|
||||
void setAutomaticTransferSuccessProperties(B builder, TransferData transferData) {
|
||||
/** Process an automatic transfer on a domain. */
|
||||
public static void setAutomaticTransferSuccessProperties(
|
||||
DomainBase.Builder<?, ?> builder, DomainTransferData transferData) {
|
||||
checkArgument(TransferStatus.PENDING.equals(transferData.getTransferStatus()));
|
||||
TransferData.Builder transferDataBuilder = transferData.asBuilder();
|
||||
DomainTransferData.Builder transferDataBuilder = transferData.asBuilder();
|
||||
transferDataBuilder.setTransferStatus(TransferStatus.SERVER_APPROVED);
|
||||
transferDataBuilder.setServerApproveEntities(null, null, null);
|
||||
if (transferData instanceof DomainTransferData) {
|
||||
((DomainTransferData.Builder) transferDataBuilder)
|
||||
.setServerApproveBillingEvent(null)
|
||||
.setServerApproveAutorenewEvent(null)
|
||||
.setServerApproveAutorenewPollMessage(null);
|
||||
}
|
||||
transferDataBuilder
|
||||
.setServerApproveEntities(null, null, null)
|
||||
.setServerApproveBillingEvent(null)
|
||||
.setServerApproveAutorenewEvent(null)
|
||||
.setServerApproveAutorenewPollMessage(null);
|
||||
builder
|
||||
.removeStatusValue(StatusValue.PENDING_TRANSFER)
|
||||
.setTransferData((T) transferDataBuilder.build())
|
||||
.setTransferData(transferDataBuilder.build())
|
||||
.setLastTransferTime(transferData.getPendingTransferExpirationTime())
|
||||
.setPersistedCurrentSponsorRegistrarId(transferData.getGainingRegistrarId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform common operations for projecting an {@link EppResource} at a given time:
|
||||
* Perform common operations for projecting a {@link Domain} at a given time:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Process an automatic transfer.
|
||||
* </ul>
|
||||
*/
|
||||
public static <
|
||||
T extends TransferData,
|
||||
E extends EppResource & ResourceWithTransferData<T>,
|
||||
B extends EppResource.Builder<?, B> & BuilderWithTransferData<T, B>>
|
||||
void projectResourceOntoBuilderAtTime(E resource, B builder, DateTime now) {
|
||||
T transferData = resource.getTransferData();
|
||||
public static void projectResourceOntoBuilderAtTime(
|
||||
DomainBase domain, DomainBase.Builder<?, ?> builder, DateTime now) {
|
||||
DomainTransferData transferData = domain.getTransferData();
|
||||
// If there's a pending transfer that has expired, process it.
|
||||
DateTime expirationTime = transferData.getPendingTransferExpirationTime();
|
||||
if (TransferStatus.PENDING.equals(transferData.getTransferStatus())
|
||||
@@ -207,36 +189,21 @@ public final class EppResourceUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of {@link VKey} for domains that reference a specified contact or host.
|
||||
*
|
||||
* <p>This is an eventually consistent query if used for the database.
|
||||
* Returns a set of {@link VKey} for domains that reference a specified host.
|
||||
*
|
||||
* @param key the referent key
|
||||
* @param now the logical time of the check
|
||||
* @param limit the maximum number of returned keys, unlimited if null
|
||||
*/
|
||||
public static ImmutableSet<VKey<Domain>> getLinkedDomainKeys(
|
||||
VKey<? extends EppResource> key, DateTime now, @Nullable Integer limit) {
|
||||
checkArgument(
|
||||
key.getKind().equals(Contact.class) || key.getKind().equals(Host.class),
|
||||
"key must be either VKey<Contact> or VKey<Host>, but it is %s",
|
||||
key);
|
||||
boolean isContactKey = key.getKind().equals(Contact.class);
|
||||
VKey<Host> key, DateTime now, @Nullable Integer limit) {
|
||||
return tm().reTransact(
|
||||
() -> {
|
||||
Query query;
|
||||
if (isContactKey) {
|
||||
query =
|
||||
tm().query(CONTACT_LINKED_DOMAIN_QUERY, String.class)
|
||||
.setParameter("fkRepoId", key)
|
||||
.setParameter("now", now);
|
||||
} else {
|
||||
query =
|
||||
tm().getEntityManager()
|
||||
.createNativeQuery(HOST_LINKED_DOMAIN_QUERY)
|
||||
.setParameter("fkRepoId", key.getKey())
|
||||
.setParameter("now", now.toDate());
|
||||
}
|
||||
Query query =
|
||||
tm().getEntityManager()
|
||||
.createNativeQuery(HOST_LINKED_DOMAIN_QUERY)
|
||||
.setParameter("fkRepoId", key.getKey())
|
||||
.setParameter("now", now.toDate());
|
||||
if (limit != null) {
|
||||
query.setMaxResults(limit);
|
||||
}
|
||||
@@ -252,12 +219,12 @@ public final class EppResourceUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given contact or host is linked to (that is, referenced by) a domain.
|
||||
* Returns whether the given host is linked to (that is, referenced by) a domain.
|
||||
*
|
||||
* @param key the referent key
|
||||
* @param now the logical time of the check
|
||||
*/
|
||||
public static boolean isLinked(VKey<? extends EppResource> key, DateTime now) {
|
||||
public static boolean isLinked(VKey<Host> key, DateTime now) {
|
||||
return !getLinkedDomainKeys(key, now, 1).isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,32 +16,26 @@ package google.registry.model;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import google.registry.model.EppResource.BuilderWithTransferData;
|
||||
import google.registry.model.EppResource.ResourceWithTransferData;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.poll.PendingActionNotificationResponse;
|
||||
import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse;
|
||||
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.transfer.DomainTransferData;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.model.transfer.TransferResponse;
|
||||
import google.registry.model.transfer.TransferResponse.ContactTransferResponse;
|
||||
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Static utility functions for resource transfers. */
|
||||
/** Static utility functions for domain transfers. */
|
||||
public final class ResourceTransferUtils {
|
||||
|
||||
private ResourceTransferUtils() {}
|
||||
@@ -50,109 +44,81 @@ public final class ResourceTransferUtils {
|
||||
private static final ImmutableSet<TransferStatus> ADD_EXDATE_STATUSES = Sets.immutableEnumSet(
|
||||
TransferStatus.PENDING, TransferStatus.CLIENT_APPROVED, TransferStatus.SERVER_APPROVED);
|
||||
|
||||
/**
|
||||
* Create a transfer response using the id and type of this resource and the specified {@link
|
||||
* TransferData}.
|
||||
*/
|
||||
/** Create a transfer response using the domain and the specified {@link DomainTransferData}. */
|
||||
public static TransferResponse createTransferResponse(
|
||||
EppResource eppResource, TransferData transferData) {
|
||||
assertIsContactOrDomain(eppResource);
|
||||
@SuppressWarnings("NonCanonicalType")
|
||||
TransferResponse.Builder<? extends TransferResponse, ?> builder;
|
||||
if (eppResource instanceof Contact) {
|
||||
builder = new ContactTransferResponse.Builder().setContactId(eppResource.getForeignKey());
|
||||
} else {
|
||||
DomainTransferData domainTransferData = (DomainTransferData) transferData;
|
||||
builder =
|
||||
new DomainTransferResponse.Builder()
|
||||
.setDomainName(eppResource.getForeignKey())
|
||||
.setExtendedRegistrationExpirationTime(
|
||||
ADD_EXDATE_STATUSES.contains(domainTransferData.getTransferStatus())
|
||||
? domainTransferData.getTransferredRegistrationExpirationTime()
|
||||
: null);
|
||||
}
|
||||
builder
|
||||
Domain domain, DomainTransferData transferData) {
|
||||
return new DomainTransferResponse.Builder()
|
||||
.setDomainName(domain.getForeignKey())
|
||||
.setExtendedRegistrationExpirationTime(
|
||||
ADD_EXDATE_STATUSES.contains(transferData.getTransferStatus())
|
||||
? transferData.getTransferredRegistrationExpirationTime()
|
||||
: null)
|
||||
.setGainingRegistrarId(transferData.getGainingRegistrarId())
|
||||
.setLosingRegistrarId(transferData.getLosingRegistrarId())
|
||||
.setPendingTransferExpirationTime(transferData.getPendingTransferExpirationTime())
|
||||
.setTransferRequestTime(transferData.getTransferRequestTime())
|
||||
.setTransferStatus(transferData.getTransferStatus());
|
||||
return builder.build();
|
||||
.setTransferStatus(transferData.getTransferStatus())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a pending action notification response indicating the resolution of a transfer.
|
||||
*
|
||||
* <p>The returned object will use the id and type of this resource, the trid of the resource's
|
||||
* last transfer request, and the specified status and date.
|
||||
* <p>The returned object will use the trid of the domain's last transfer request, and the
|
||||
* specified status and date.
|
||||
*/
|
||||
public static PendingActionNotificationResponse createPendingTransferNotificationResponse(
|
||||
EppResource eppResource,
|
||||
Trid transferRequestTrid,
|
||||
boolean actionResult,
|
||||
DateTime processedDate) {
|
||||
assertIsContactOrDomain(eppResource);
|
||||
return eppResource instanceof Contact
|
||||
? ContactPendingActionNotificationResponse.create(
|
||||
eppResource.getForeignKey(), actionResult, transferRequestTrid, processedDate)
|
||||
: DomainPendingActionNotificationResponse.create(
|
||||
eppResource.getForeignKey(), actionResult, transferRequestTrid, processedDate);
|
||||
}
|
||||
|
||||
private static void assertIsContactOrDomain(EppResource eppResource) {
|
||||
checkState(eppResource instanceof Contact || eppResource instanceof Domain);
|
||||
Domain domain, Trid transferRequestTrid, boolean actionResult, DateTime processedDate) {
|
||||
return DomainPendingActionNotificationResponse.create(
|
||||
domain.getDomainName(), actionResult, transferRequestTrid, processedDate);
|
||||
}
|
||||
|
||||
/** If there is a transfer out, delete the server-approve entities and enqueue a poll message. */
|
||||
public static <R extends EppResource & ResourceWithTransferData>
|
||||
void handlePendingTransferOnDelete(
|
||||
R resource, R newResource, DateTime now, HistoryEntry historyEntry) {
|
||||
if (resource.getStatusValues().contains(StatusValue.PENDING_TRANSFER)) {
|
||||
TransferData oldTransferData = resource.getTransferData();
|
||||
tm().delete(oldTransferData.getServerApproveEntities());
|
||||
tm().put(
|
||||
new PollMessage.OneTime.Builder()
|
||||
.setRegistrarId(oldTransferData.getGainingRegistrarId())
|
||||
.setEventTime(now)
|
||||
.setMsg(TransferStatus.SERVER_CANCELLED.getMessage())
|
||||
.setResponseData(
|
||||
ImmutableList.of(
|
||||
createTransferResponse(newResource, newResource.getTransferData()),
|
||||
createPendingTransferNotificationResponse(
|
||||
resource, oldTransferData.getTransferRequestTrid(), false, now)))
|
||||
.setHistoryEntry(historyEntry)
|
||||
.build());
|
||||
public static void handlePendingTransferOnDelete(
|
||||
Domain domain, Domain newDomain, DateTime now, HistoryEntry historyEntry) {
|
||||
if (!domain.getStatusValues().contains(StatusValue.PENDING_TRANSFER)) {
|
||||
return;
|
||||
}
|
||||
TransferData oldTransferData = domain.getTransferData();
|
||||
tm().delete(oldTransferData.getServerApproveEntities());
|
||||
tm().put(
|
||||
new PollMessage.OneTime.Builder()
|
||||
.setRegistrarId(oldTransferData.getGainingRegistrarId())
|
||||
.setEventTime(now)
|
||||
.setMsg(TransferStatus.SERVER_CANCELLED.getMessage())
|
||||
.setResponseData(
|
||||
ImmutableList.of(
|
||||
createTransferResponse(newDomain, newDomain.getTransferData()),
|
||||
createPendingTransferNotificationResponse(
|
||||
domain, oldTransferData.getTransferRequestTrid(), false, now)))
|
||||
.setHistoryEntry(historyEntry)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a resource into a builder with its pending transfer resolved.
|
||||
* Turn a domain into a builder with its pending transfer resolved.
|
||||
*
|
||||
* <p>This removes the {@link StatusValue#PENDING_TRANSFER} status, sets the {@link
|
||||
* TransferStatus}, clears all the server-approve fields on the {@link TransferData}, and sets the
|
||||
* expiration time of the last pending transfer to now.
|
||||
*/
|
||||
private static <
|
||||
R extends EppResource & ResourceWithTransferData,
|
||||
B extends EppResource.Builder<R, B> & BuilderWithTransferData<TransferData, B>>
|
||||
B resolvePendingTransfer(R resource, TransferStatus transferStatus, DateTime now) {
|
||||
private static Domain.Builder resolvePendingTransfer(
|
||||
Domain domain, TransferStatus transferStatus, DateTime now) {
|
||||
checkArgument(
|
||||
resource.getStatusValues().contains(StatusValue.PENDING_TRANSFER),
|
||||
"Resource is not in pending transfer status.");
|
||||
checkArgument(!resource.getTransferData().isEmpty(), "No old transfer data to resolve.");
|
||||
@SuppressWarnings("unchecked")
|
||||
B builder = (B) resource.asBuilder();
|
||||
domain.getStatusValues().contains(StatusValue.PENDING_TRANSFER),
|
||||
"Domain is not in pending transfer status.");
|
||||
checkArgument(!domain.getTransferData().isEmpty(), "No old transfer data to resolve.");
|
||||
|
||||
return builder
|
||||
return domain
|
||||
.asBuilder()
|
||||
.removeStatusValue(StatusValue.PENDING_TRANSFER)
|
||||
.setTransferData(
|
||||
(TransferData)
|
||||
resource
|
||||
.getTransferData()
|
||||
.copyConstantFieldsToBuilder()
|
||||
.setTransferStatus(transferStatus)
|
||||
.setPendingTransferExpirationTime(checkNotNull(now))
|
||||
.build());
|
||||
domain
|
||||
.getTransferData()
|
||||
.copyConstantFieldsToBuilder()
|
||||
.setTransferStatus(transferStatus)
|
||||
.setPendingTransferExpirationTime(checkNotNull(now))
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,15 +129,13 @@ public final class ResourceTransferUtils {
|
||||
* client id, and sets the last transfer time and the expiration time of the last pending transfer
|
||||
* to now.
|
||||
*/
|
||||
public static <
|
||||
R extends EppResource & ResourceWithTransferData,
|
||||
B extends EppResource.Builder<R, B> & BuilderWithTransferData<TransferData, B>>
|
||||
R approvePendingTransfer(R resource, TransferStatus transferStatus, DateTime now) {
|
||||
public static Domain approvePendingTransfer(
|
||||
Domain domain, TransferStatus transferStatus, DateTime now) {
|
||||
checkArgument(transferStatus.isApproved(), "Not an approval transfer status");
|
||||
B builder = resolvePendingTransfer(resource, transferStatus, now);
|
||||
Domain.Builder builder = resolvePendingTransfer(domain, transferStatus, now);
|
||||
return builder
|
||||
.setLastTransferTime(now)
|
||||
.setPersistedCurrentSponsorRegistrarId(resource.getTransferData().getGainingRegistrarId())
|
||||
.setPersistedCurrentSponsorRegistrarId(domain.getTransferData().getGainingRegistrarId())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -183,10 +147,10 @@ public final class ResourceTransferUtils {
|
||||
* expiration time of the last pending transfer to now, sets the last EPP update time to now, and
|
||||
* sets the last EPP update client id to the given client id.
|
||||
*/
|
||||
public static <R extends EppResource & ResourceWithTransferData> R denyPendingTransfer(
|
||||
R resource, TransferStatus transferStatus, DateTime now, String lastEppUpdateRegistrarId) {
|
||||
public static Domain denyPendingTransfer(
|
||||
Domain domain, TransferStatus transferStatus, DateTime now, String lastEppUpdateRegistrarId) {
|
||||
checkArgument(transferStatus.isDenied(), "Not a denial transfer status");
|
||||
return resolvePendingTransfer(resource, transferStatus, now)
|
||||
return resolvePendingTransfer(domain, transferStatus, now)
|
||||
.setLastEppUpdateTime(now)
|
||||
.setLastEppUpdateRegistrarId(lastEppUpdateRegistrarId)
|
||||
.build();
|
||||
|
||||
@@ -64,6 +64,7 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
|
||||
/** The names of the feature flags that can be individually set. */
|
||||
public enum FeatureName {
|
||||
|
||||
/** Feature flag name used for testing only. */
|
||||
TEST_FEATURE(FeatureStatus.INACTIVE),
|
||||
|
||||
@@ -76,7 +77,10 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
/**
|
||||
* If we're including the upcoming domain drop date in the exported list of registered domains.
|
||||
*/
|
||||
INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS(FeatureStatus.INACTIVE);
|
||||
INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS(FeatureStatus.INACTIVE),
|
||||
|
||||
/** If we're prohibiting the inclusion of the contact object URI on login. */
|
||||
PROHIBIT_CONTACT_OBJECTS_ON_LOGIN(FeatureStatus.INACTIVE);
|
||||
|
||||
private final FeatureStatus defaultStatus;
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Index;
|
||||
import jakarta.persistence.Table;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* A persistable contact resource including mutable and non-mutable fields.
|
||||
@@ -58,11 +57,6 @@ public class Contact extends ContactBase implements ForeignKeyedEppResource {
|
||||
return super.getRepoId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Contact cloneProjectedAtTime(DateTime now) {
|
||||
return ContactBase.cloneContactProjectedAtTime(this, now);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
|
||||
@@ -16,7 +16,6 @@ package google.registry.model.contact;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.model.EppResourceUtils.projectResourceOntoBuilderAtTime;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.EppResource;
|
||||
@@ -254,17 +253,8 @@ public class ContactBase extends EppResource
|
||||
|
||||
@Override
|
||||
public ContactBase cloneProjectedAtTime(DateTime now) {
|
||||
return cloneContactProjectedAtTime(this, now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the contact (or subclass). A separate static method so that we can pass in and return a
|
||||
* T without the compiler complaining.
|
||||
*/
|
||||
protected static <T extends ContactBase> T cloneContactProjectedAtTime(T contact, DateTime now) {
|
||||
Builder builder = contact.asBuilder();
|
||||
projectResourceOntoBuilderAtTime(contact, builder, now);
|
||||
return (T) builder.build();
|
||||
// Contacts no longer exist and thus do not need to be projected
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,20 +16,18 @@ package google.registry.model.domain;
|
||||
|
||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static google.registry.util.CollectionUtils.difference;
|
||||
import static google.registry.util.CollectionUtils.forceEmptyToNull;
|
||||
import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
import static google.registry.util.CollectionUtils.nullSafeImmutableCopy;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
||||
import static google.registry.util.CollectionUtils.union;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.flows.EppException.ParameterValuePolicyErrorException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.contact.Contact;
|
||||
@@ -67,7 +65,8 @@ public class DomainCommand {
|
||||
*/
|
||||
public interface CreateOrUpdate<T extends CreateOrUpdate<T>> extends SingleResourceCommand {
|
||||
/** Creates a copy of this command with hard links to hosts and contacts. */
|
||||
T cloneAndLinkReferences(DateTime now) throws InvalidReferencesException;
|
||||
T cloneAndLinkReferences(DateTime now)
|
||||
throws InvalidReferencesException, ParameterValuePolicyErrorException;
|
||||
}
|
||||
|
||||
/** The fields on "chgType" from <a href="http://tools.ietf.org/html/rfc5731">RFC5731</a>. */
|
||||
@@ -171,26 +170,15 @@ public class DomainCommand {
|
||||
|
||||
/** Creates a copy of this {@link Create} with hard links to hosts and contacts. */
|
||||
@Override
|
||||
public Create cloneAndLinkReferences(DateTime now) throws InvalidReferencesException {
|
||||
public Create cloneAndLinkReferences(DateTime now)
|
||||
throws InvalidReferencesException, ParameterValuePolicyErrorException {
|
||||
Create clone = clone(this);
|
||||
clone.nameservers = linkHosts(clone.nameserverHostNames, now);
|
||||
if (registrantContactId == null) {
|
||||
clone.contacts = linkContacts(clone.foreignKeyedDesignatedContacts, now);
|
||||
} else {
|
||||
// Load the registrant and contacts in one shot.
|
||||
ForeignKeyedDesignatedContact registrantPlaceholder = new ForeignKeyedDesignatedContact();
|
||||
registrantPlaceholder.contactId = clone.registrantContactId;
|
||||
registrantPlaceholder.type = DesignatedContact.Type.REGISTRANT;
|
||||
Set<DesignatedContact> contacts = linkContacts(
|
||||
union(nullToEmpty(clone.foreignKeyedDesignatedContacts), registrantPlaceholder),
|
||||
now);
|
||||
for (DesignatedContact contact : contacts) {
|
||||
if (DesignatedContact.Type.REGISTRANT.equals(contact.getType())) {
|
||||
clone.registrant = contact.getContactKey();
|
||||
clone.contacts = forceEmptyToNull(difference(contacts, contact));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (registrantContactId != null) {
|
||||
throw new RegistrantProhibitedException();
|
||||
}
|
||||
if (!isNullOrEmpty(foreignKeyedDesignatedContacts)) {
|
||||
throw new ContactsProhibitedException();
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
@@ -369,10 +357,13 @@ public class DomainCommand {
|
||||
}
|
||||
|
||||
/** Creates a copy of this {@link AddRemove} with hard links to hosts and contacts. */
|
||||
private AddRemove cloneAndLinkReferences(DateTime now) throws InvalidReferencesException {
|
||||
private AddRemove cloneAndLinkReferences(DateTime now)
|
||||
throws InvalidReferencesException, ContactsProhibitedException {
|
||||
AddRemove clone = clone(this);
|
||||
clone.nameservers = linkHosts(clone.nameserverHostNames, now);
|
||||
clone.contacts = linkContacts(clone.foreignKeyedDesignatedContacts, now);
|
||||
if (!isNullOrEmpty(foreignKeyedDesignatedContacts)) {
|
||||
throw new ContactsProhibitedException();
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
@@ -380,16 +371,11 @@ public class DomainCommand {
|
||||
/** The inner change type on a domain update command. */
|
||||
@XmlType(propOrder = {"registrantContactId", "authInfo"})
|
||||
public static class Change extends DomainCreateOrChange<Domain.Builder> {
|
||||
/** Creates a copy of this {@link Change} with hard links to hosts and contacts. */
|
||||
Change cloneAndLinkReferences(DateTime now) throws InvalidReferencesException {
|
||||
Change cloneAndLinkReferences() throws RegistrantProhibitedException {
|
||||
Change clone = clone(this);
|
||||
clone.registrant =
|
||||
Strings.isNullOrEmpty(clone.registrantContactId)
|
||||
? null
|
||||
: getOnlyElement(
|
||||
loadByForeignKeysCached(
|
||||
ImmutableSet.of(clone.registrantContactId), Contact.class, now)
|
||||
.values());
|
||||
if (clone.registrantContactId != null) {
|
||||
throw new RegistrantProhibitedException();
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
@@ -401,11 +387,12 @@ public class DomainCommand {
|
||||
* of those classes, which is harmless because the getters do that anyways.
|
||||
*/
|
||||
@Override
|
||||
public Update cloneAndLinkReferences(DateTime now) throws InvalidReferencesException {
|
||||
public Update cloneAndLinkReferences(DateTime now)
|
||||
throws InvalidReferencesException, ParameterValuePolicyErrorException {
|
||||
Update clone = clone(this);
|
||||
clone.innerAdd = clone.getInnerAdd().cloneAndLinkReferences(now);
|
||||
clone.innerRemove = clone.getInnerRemove().cloneAndLinkReferences(now);
|
||||
clone.innerChange = clone.getInnerChange().cloneAndLinkReferences(now);
|
||||
clone.innerChange = clone.getInnerChange().cloneAndLinkReferences();
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
@@ -415,37 +402,17 @@ public class DomainCommand {
|
||||
if (hostNames == null) {
|
||||
return null;
|
||||
}
|
||||
return ImmutableSet.copyOf(loadByForeignKeysCached(hostNames, Host.class, now).values());
|
||||
return ImmutableSet.copyOf(loadByForeignKeysCached(hostNames, now).values());
|
||||
}
|
||||
|
||||
private static Set<DesignatedContact> linkContacts(
|
||||
Set<ForeignKeyedDesignatedContact> contacts, DateTime now) throws InvalidReferencesException {
|
||||
if (contacts == null) {
|
||||
return null;
|
||||
}
|
||||
ImmutableSet.Builder<String> foreignKeys = new ImmutableSet.Builder<>();
|
||||
for (ForeignKeyedDesignatedContact contact : contacts) {
|
||||
foreignKeys.add(contact.contactId);
|
||||
}
|
||||
ImmutableMap<String, VKey<Contact>> loadedContacts =
|
||||
loadByForeignKeysCached(foreignKeys.build(), Contact.class, now);
|
||||
ImmutableSet.Builder<DesignatedContact> linkedContacts = new ImmutableSet.Builder<>();
|
||||
for (ForeignKeyedDesignatedContact contact : contacts) {
|
||||
linkedContacts.add(
|
||||
DesignatedContact.create(contact.type, loadedContacts.get(contact.contactId)));
|
||||
}
|
||||
return linkedContacts.build();
|
||||
}
|
||||
|
||||
/** Loads keys to cached EPP resources by their foreign keys. */
|
||||
private static <T extends EppResource> ImmutableMap<String, VKey<T>> loadByForeignKeysCached(
|
||||
final Set<String> foreignKeys, final Class<T> clazz, final DateTime now)
|
||||
throws InvalidReferencesException {
|
||||
ImmutableMap<String, VKey<T>> fks =
|
||||
ForeignKeyUtils.loadKeysByCacheIfEnabled(clazz, foreignKeys, now);
|
||||
/** Loads host keys to cached EPP resources by their foreign keys. */
|
||||
private static ImmutableMap<String, VKey<Host>> loadByForeignKeysCached(
|
||||
final Set<String> foreignKeys, final DateTime now) throws InvalidReferencesException {
|
||||
ImmutableMap<String, VKey<Host>> fks =
|
||||
ForeignKeyUtils.loadKeysByCacheIfEnabled(Host.class, foreignKeys, now);
|
||||
if (!fks.keySet().equals(foreignKeys)) {
|
||||
throw new InvalidReferencesException(
|
||||
clazz, ImmutableSet.copyOf(difference(foreignKeys, fks.keySet())));
|
||||
Host.class, ImmutableSet.copyOf(difference(foreignKeys, fks.keySet())));
|
||||
}
|
||||
return fks;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
@Access(AccessType.FIELD)
|
||||
public abstract class DomainDsDataBase extends ImmutableObject implements UnsafeSerializable {
|
||||
|
||||
@XmlTransient @Transient String domainRepoId;
|
||||
@XmlTransient @Transient @Insignificant String domainRepoId;
|
||||
|
||||
/** The identifier for this particular key in the domain. */
|
||||
@Transient int keyTag;
|
||||
|
||||
@@ -24,6 +24,7 @@ import jakarta.xml.bind.annotation.XmlElement;
|
||||
import jakarta.xml.bind.annotation.XmlRootElement;
|
||||
import jakarta.xml.bind.annotation.XmlTransient;
|
||||
import jakarta.xml.bind.annotation.XmlType;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/** The EPP secDNS extension that may be present on domain update commands. */
|
||||
@@ -55,16 +56,16 @@ public class SecDnsUpdateExtension extends ImmutableObject implements CommandExt
|
||||
return urgent;
|
||||
}
|
||||
|
||||
public Remove getRemove() {
|
||||
return remove;
|
||||
public Optional<Remove> getRemove() {
|
||||
return Optional.ofNullable(remove);
|
||||
}
|
||||
|
||||
public Add getAdd() {
|
||||
return add;
|
||||
public Optional<Add> getAdd() {
|
||||
return Optional.ofNullable(add);
|
||||
}
|
||||
|
||||
public Change getChange() {
|
||||
return change;
|
||||
public Optional<Change> getChange() {
|
||||
return Optional.ofNullable(change);
|
||||
}
|
||||
|
||||
@XmlTransient
|
||||
|
||||
@@ -20,9 +20,14 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.flows.FeeExtensionXmlTagNormalizer;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.domain.fee.FeeCheckResponseExtension;
|
||||
import google.registry.model.domain.fee.FeeTransformResponseExtension;
|
||||
import google.registry.model.domain.fee06.FeeInfoResponseExtensionV06;
|
||||
import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import google.registry.xml.ValidationMode;
|
||||
import google.registry.xml.XmlException;
|
||||
@@ -98,8 +103,31 @@ public class EppXmlTransformer {
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
|
||||
private static boolean hasFeeExtension(EppOutput eppOutput) {
|
||||
if (!eppOutput.isResponse()) {
|
||||
return false;
|
||||
}
|
||||
return eppOutput.getResponse().getExtensions().stream()
|
||||
.map(EppResponse.ResponseExtension::getClass)
|
||||
.filter(EppXmlTransformer::isFeeExtension)
|
||||
.findAny()
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static boolean isFeeExtension(Class<?> clazz) {
|
||||
return FeeCheckResponseExtension.class.isAssignableFrom(clazz)
|
||||
|| FeeTransformResponseExtension.class.isAssignableFrom(clazz)
|
||||
|| FeeInfoResponseExtensionV06.class.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
public static byte[] marshal(EppOutput root, ValidationMode validation) throws XmlException {
|
||||
return marshal(OUTPUT_TRANSFORMER, root, validation);
|
||||
byte[] bytes = marshal(OUTPUT_TRANSFORMER, root, validation);
|
||||
if (!RegistryEnvironment.PRODUCTION.equals(RegistryEnvironment.get())
|
||||
&& hasFeeExtension(root)) {
|
||||
return FeeExtensionXmlTagNormalizer.normalize(new String(bytes, UTF_8)).getBytes(UTF_8);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -17,6 +17,8 @@ package google.registry.model.eppcommon;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Maps.uniqueIndex;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.VerifyException;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.domain.fee06.FeeCheckCommandExtensionV06;
|
||||
@@ -33,6 +35,7 @@ import google.registry.model.domain.rgp.RgpUpdateExtension;
|
||||
import google.registry.model.domain.secdns.SecDnsCreateExtension;
|
||||
import google.registry.model.eppinput.EppInput.CommandExtension;
|
||||
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import jakarta.xml.bind.annotation.XmlSchema;
|
||||
import java.util.EnumSet;
|
||||
@@ -44,15 +47,17 @@ public class ProtocolDefinition {
|
||||
public static final String LANGUAGE = "en";
|
||||
|
||||
public static final ImmutableSet<String> SUPPORTED_OBJECT_SERVICES =
|
||||
ImmutableSet.of(
|
||||
"urn:ietf:params:xml:ns:host-1.0",
|
||||
"urn:ietf:params:xml:ns:domain-1.0",
|
||||
"urn:ietf:params:xml:ns:contact-1.0");
|
||||
ImmutableSet.of("urn:ietf:params:xml:ns:host-1.0", "urn:ietf:params:xml:ns:domain-1.0");
|
||||
|
||||
public static final ImmutableSet<String> SUPPORTED_OBJECT_SERVICES_WITH_CONTACT =
|
||||
new ImmutableSet.Builder<String>()
|
||||
.addAll(SUPPORTED_OBJECT_SERVICES)
|
||||
.add("urn:ietf:params:xml:ns:contact-1.0")
|
||||
.build();
|
||||
|
||||
/** Enum representing which environments should have which service extensions enabled. */
|
||||
private enum ServiceExtensionVisibility {
|
||||
ALL,
|
||||
ONLY_IN_PRODUCTION,
|
||||
ONLY_IN_NON_PRODUCTION,
|
||||
NONE
|
||||
}
|
||||
@@ -83,6 +88,7 @@ public class ProtocolDefinition {
|
||||
private final Class<? extends CommandExtension> commandExtensionClass;
|
||||
private final Class<? extends ResponseExtension> responseExtensionClass;
|
||||
private final String uri;
|
||||
private final String xmlTag;
|
||||
private final ServiceExtensionVisibility visibility;
|
||||
|
||||
ServiceExtension(
|
||||
@@ -92,6 +98,7 @@ public class ProtocolDefinition {
|
||||
this.commandExtensionClass = commandExtensionClass;
|
||||
this.responseExtensionClass = responseExtensionClass;
|
||||
this.uri = getCommandExtensionUri(commandExtensionClass);
|
||||
this.xmlTag = getCommandExtensionXmlTag(commandExtensionClass);
|
||||
this.visibility = visibility;
|
||||
}
|
||||
|
||||
@@ -107,15 +114,30 @@ public class ProtocolDefinition {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public String getXmlTag() {
|
||||
return xmlTag;
|
||||
}
|
||||
|
||||
/** Returns the namespace URI of the command extension class. */
|
||||
public static String getCommandExtensionUri(Class<? extends CommandExtension> clazz) {
|
||||
return clazz.getPackage().getAnnotation(XmlSchema.class).namespace();
|
||||
}
|
||||
|
||||
private boolean isVisible() {
|
||||
/** Returns the XML tag for this extension in the response message. */
|
||||
public static String getCommandExtensionXmlTag(Class<? extends CommandExtension> clazz) {
|
||||
var xmlSchema = clazz.getPackage().getAnnotation(XmlSchema.class);
|
||||
var xmlns = xmlSchema.xmlns();
|
||||
if (xmlns == null || xmlns.length != 1) {
|
||||
throw new VerifyException(
|
||||
String.format(
|
||||
"Expecting exactly one NS declaration in %s", clazz.getPackage().getName()));
|
||||
}
|
||||
return xmlns[0].prefix();
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
return switch (visibility) {
|
||||
case ALL -> true;
|
||||
case ONLY_IN_PRODUCTION -> RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION);
|
||||
case ONLY_IN_NON_PRODUCTION ->
|
||||
!RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION);
|
||||
case NONE -> false;
|
||||
@@ -137,14 +159,25 @@ public class ProtocolDefinition {
|
||||
}
|
||||
|
||||
/** A set of all the visible extension URIs. */
|
||||
private static final ImmutableSet<String> visibleServiceExtensionUris =
|
||||
EnumSet.allOf(ServiceExtension.class).stream()
|
||||
.filter(ServiceExtension::isVisible)
|
||||
.map(ServiceExtension::getUri)
|
||||
.collect(toImmutableSet());
|
||||
// TODO(gbrodman): make this final when we can actually remove the old fee extensions and aren't
|
||||
// relying on switching by environment
|
||||
@NonFinalForTesting private static ImmutableSet<String> visibleServiceExtensionUris;
|
||||
|
||||
static {
|
||||
reloadServiceExtensionUris();
|
||||
}
|
||||
|
||||
/** Return the set of all visible service extension URIs. */
|
||||
public static ImmutableSet<String> getVisibleServiceExtensionUris() {
|
||||
return visibleServiceExtensionUris;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void reloadServiceExtensionUris() {
|
||||
visibleServiceExtensionUris =
|
||||
EnumSet.allOf(ServiceExtension.class).stream()
|
||||
.filter(ServiceExtension::isVisible)
|
||||
.map(ServiceExtension::getUri)
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ public class EppInput extends ImmutableObject {
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
/** A tag that goes inside of an EPP {@literal <command>}. */
|
||||
/** A tag that goes inside an EPP {@literal <command>}. */
|
||||
public static class InnerCommand extends ImmutableObject {}
|
||||
|
||||
/** A command that has an extension inside of it. */
|
||||
|
||||
@@ -26,8 +26,6 @@ import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.UnsafeSerializable;
|
||||
import google.registry.model.annotations.ExternalMessagingName;
|
||||
import google.registry.model.annotations.IdAllocation;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.DomainRenewData;
|
||||
@@ -99,7 +97,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||
/** Indicates the type of entity the poll message is for. */
|
||||
public enum Type {
|
||||
DOMAIN(1L, Domain.class),
|
||||
CONTACT(2L, Contact.class),
|
||||
// Contacts would be 2L but have since been removed. Host is kept at 3 for consistency.
|
||||
HOST(3L, Host.class);
|
||||
|
||||
private final long id;
|
||||
@@ -180,16 +178,6 @@ public abstract class PollMessage extends ImmutableObject
|
||||
return domainRepoId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contact repo id.
|
||||
*
|
||||
* <p>This may only be used on a {@link Contact} poll event.
|
||||
*/
|
||||
public String getContactRepoId() {
|
||||
checkArgument(getType() == Type.CONTACT);
|
||||
return contactRepoId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host repo id.
|
||||
*
|
||||
@@ -216,7 +204,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return domainRepoId != null ? Type.DOMAIN : contactRepoId != null ? Type.CONTACT : Type.HOST;
|
||||
return domainRepoId != null ? Type.DOMAIN : Type.HOST;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -272,12 +260,6 @@ public abstract class PollMessage extends ImmutableObject
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setContactHistoryId(HistoryEntryId historyId) {
|
||||
getInstance().contactRepoId = historyId.getRepoId();
|
||||
getInstance().contactHistoryRevisionId = historyId.getRevisionId();
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setHostHistoryId(HistoryEntryId historyId) {
|
||||
getInstance().hostRepoId = historyId.getRepoId();
|
||||
getInstance().hostHistoryRevisionId = historyId.getRevisionId();
|
||||
@@ -290,9 +272,6 @@ public abstract class PollMessage extends ImmutableObject
|
||||
if (history instanceof DomainHistory) {
|
||||
return setDomainHistoryId(historyId);
|
||||
}
|
||||
if (history instanceof ContactHistory) {
|
||||
return setContactHistoryId(historyId);
|
||||
}
|
||||
if (history instanceof HostHistory) {
|
||||
return setHostHistoryId(historyId);
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ import google.registry.model.EppResource;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.UnsafeSerializable;
|
||||
import google.registry.model.annotations.IdAllocation;
|
||||
import google.registry.model.contact.ContactBase;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
@@ -348,8 +346,6 @@ public abstract class HistoryEntry extends ImmutableObject
|
||||
HistoryEntry.Builder<? extends HistoryEntry, ?> createBuilderForResource(E parent) {
|
||||
if (parent instanceof DomainBase) {
|
||||
return new DomainHistory.Builder().setDomain((DomainBase) parent);
|
||||
} else if (parent instanceof ContactBase) {
|
||||
return new ContactHistory.Builder().setContact((ContactBase) parent);
|
||||
} else if (parent instanceof HostBase) {
|
||||
return new HostHistory.Builder().setHost((HostBase) parent);
|
||||
} else {
|
||||
|
||||
@@ -25,8 +25,6 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Streams;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.host.Host;
|
||||
@@ -46,8 +44,6 @@ public class HistoryEntryDao {
|
||||
public static ImmutableMap<Class<? extends EppResource>, Class<? extends HistoryEntry>>
|
||||
RESOURCE_TYPES_TO_HISTORY_TYPES =
|
||||
ImmutableMap.of(
|
||||
Contact.class,
|
||||
ContactHistory.class,
|
||||
Domain.class,
|
||||
DomainHistory.class,
|
||||
Host.class,
|
||||
@@ -59,7 +55,6 @@ public class HistoryEntryDao {
|
||||
return tm().transact(
|
||||
() ->
|
||||
new ImmutableList.Builder<HistoryEntry>()
|
||||
.addAll(loadAllHistoryObjects(ContactHistory.class, afterTime, beforeTime))
|
||||
.addAll(loadAllHistoryObjects(DomainHistory.class, afterTime, beforeTime))
|
||||
.addAll(loadAllHistoryObjects(HostHistory.class, afterTime, beforeTime))
|
||||
.build());
|
||||
@@ -121,7 +116,6 @@ public class HistoryEntryDao {
|
||||
return tm().reTransact(
|
||||
() ->
|
||||
Streams.concat(
|
||||
loadHistoryObjectByRegistrarsInternal(ContactHistory.class, registrarIds),
|
||||
loadHistoryObjectByRegistrarsInternal(DomainHistory.class, registrarIds),
|
||||
loadHistoryObjectByRegistrarsInternal(HostHistory.class, registrarIds))
|
||||
.sorted(Comparator.comparing(HistoryEntry::getModificationTime))
|
||||
|
||||
@@ -24,11 +24,9 @@ import google.registry.batch.DeleteLoadTestDataAction;
|
||||
import google.registry.batch.DeleteProberDataAction;
|
||||
import google.registry.batch.ExpandBillingRecurrencesAction;
|
||||
import google.registry.batch.RelockDomainAction;
|
||||
import google.registry.batch.RemoveAllDomainContactsAction;
|
||||
import google.registry.batch.ResaveAllEppResourcesPipelineAction;
|
||||
import google.registry.batch.ResaveEntityAction;
|
||||
import google.registry.batch.SendExpiringCertificateNotificationEmailAction;
|
||||
import google.registry.batch.WipeOutContactHistoryPiiAction;
|
||||
import google.registry.bsa.BsaDownloadAction;
|
||||
import google.registry.bsa.BsaRefreshAction;
|
||||
import google.registry.bsa.BsaValidateAction;
|
||||
@@ -276,8 +274,6 @@ interface RequestComponent {
|
||||
|
||||
ReadinessProbeActionFrontend readinessProbeActionFrontend();
|
||||
|
||||
RemoveAllDomainContactsAction removeAllDomainContactsAction();
|
||||
|
||||
RdapAutnumAction rdapAutnumAction();
|
||||
|
||||
RdapDomainAction rdapDomainAction();
|
||||
@@ -350,8 +346,6 @@ interface RequestComponent {
|
||||
|
||||
VerifyOteAction verifyOteAction();
|
||||
|
||||
WipeOutContactHistoryPiiAction wipeOutContactHistoryPiiAction();
|
||||
|
||||
@Subcomponent.Builder
|
||||
abstract class Builder implements RequestComponentBuilder<RequestComponent> {
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.monitoring.whitebox;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.monitoring.metrics.LabelDescriptor;
|
||||
import com.google.monitoring.metrics.MetricRegistry;
|
||||
import com.google.monitoring.metrics.MetricRegistryImpl;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryMXBean;
|
||||
import java.lang.management.MemoryUsage;
|
||||
|
||||
/** Exposes JVM metrics. */
|
||||
@Singleton
|
||||
class JvmMetrics {
|
||||
|
||||
private static final ImmutableSet<LabelDescriptor> TYPE_LABEL_SET =
|
||||
ImmutableSet.of(LabelDescriptor.create("type", "Memory type (e.g., heap, non_heap)"));
|
||||
private final MemoryMXBean memoryMxBean;
|
||||
|
||||
@Inject
|
||||
JvmMetrics() {
|
||||
this(ManagementFactory.getMemoryMXBean());
|
||||
}
|
||||
|
||||
/** Constructor for testing. */
|
||||
JvmMetrics(MemoryMXBean memoryMxBean) {
|
||||
this.memoryMxBean = memoryMxBean;
|
||||
}
|
||||
|
||||
/** Registers JVM gauges with the default registry. */
|
||||
void register() {
|
||||
MetricRegistry registry = MetricRegistryImpl.getDefault();
|
||||
|
||||
registry.newGauge(
|
||||
"/jvm/memory/used",
|
||||
"Current memory usage in bytes",
|
||||
"bytes",
|
||||
TYPE_LABEL_SET,
|
||||
(Supplier<ImmutableMap<ImmutableList<String>, Long>>) this::getUsedMemory,
|
||||
Long.class);
|
||||
|
||||
registry.newGauge(
|
||||
"/jvm/memory/committed",
|
||||
"Committed memory in bytes",
|
||||
"bytes",
|
||||
TYPE_LABEL_SET,
|
||||
(Supplier<ImmutableMap<ImmutableList<String>, Long>>) this::getCommittedMemory,
|
||||
Long.class);
|
||||
|
||||
registry.newGauge(
|
||||
"/jvm/memory/max",
|
||||
"Maximum memory in bytes",
|
||||
"bytes",
|
||||
TYPE_LABEL_SET,
|
||||
(Supplier<ImmutableMap<ImmutableList<String>, Long>>) this::getMaxMemory,
|
||||
Long.class);
|
||||
}
|
||||
|
||||
ImmutableMap<ImmutableList<String>, Long> getUsedMemory() {
|
||||
MemoryUsage heapUsage = memoryMxBean.getHeapMemoryUsage();
|
||||
MemoryUsage nonHeapUsage = memoryMxBean.getNonHeapMemoryUsage();
|
||||
|
||||
return ImmutableMap.of(
|
||||
ImmutableList.of("heap"), heapUsage.getUsed(),
|
||||
ImmutableList.of("non_heap"), nonHeapUsage.getUsed());
|
||||
}
|
||||
|
||||
ImmutableMap<ImmutableList<String>, Long> getCommittedMemory() {
|
||||
MemoryUsage heapUsage = memoryMxBean.getHeapMemoryUsage();
|
||||
MemoryUsage nonHeapUsage = memoryMxBean.getNonHeapMemoryUsage();
|
||||
|
||||
return ImmutableMap.of(
|
||||
ImmutableList.of("heap"), heapUsage.getCommitted(),
|
||||
ImmutableList.of("non_heap"), nonHeapUsage.getCommitted());
|
||||
}
|
||||
|
||||
ImmutableMap<ImmutableList<String>, Long> getMaxMemory() {
|
||||
MemoryUsage heapUsage = memoryMxBean.getHeapMemoryUsage();
|
||||
MemoryUsage nonHeapUsage = memoryMxBean.getNonHeapMemoryUsage();
|
||||
|
||||
return ImmutableMap.of(
|
||||
ImmutableList.of("heap"), heapUsage.getMax(),
|
||||
ImmutableList.of("non_heap"), nonHeapUsage.getMax());
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ import jakarta.inject.Named;
|
||||
import jakarta.inject.Singleton;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/** Dagger module for Google Stackdriver service connection objects. */
|
||||
/** Dagger module for monitoring and Google Stackdriver service connection objects. */
|
||||
@Module
|
||||
public final class StackdriverModule {
|
||||
|
||||
@@ -77,7 +77,11 @@ public final class StackdriverModule {
|
||||
|
||||
@Provides
|
||||
static MetricReporter provideMetricReporter(
|
||||
MetricWriter metricWriter, @Config("metricsWriteInterval") Duration writeInterval) {
|
||||
MetricWriter metricWriter,
|
||||
@Config("metricsWriteInterval") Duration writeInterval,
|
||||
JvmMetrics jvmMetrics) {
|
||||
jvmMetrics.register();
|
||||
|
||||
return new MetricReporter(
|
||||
metricWriter,
|
||||
writeInterval.getStandardSeconds(),
|
||||
|
||||
@@ -14,21 +14,346 @@
|
||||
|
||||
package google.registry.mosapi;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
|
||||
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
|
||||
import com.google.api.services.monitoring.v3.Monitoring;
|
||||
import com.google.api.services.monitoring.v3.model.CreateTimeSeriesRequest;
|
||||
import com.google.api.services.monitoring.v3.model.LabelDescriptor;
|
||||
import com.google.api.services.monitoring.v3.model.Metric;
|
||||
import com.google.api.services.monitoring.v3.model.MetricDescriptor;
|
||||
import com.google.api.services.monitoring.v3.model.MonitoredResource;
|
||||
import com.google.api.services.monitoring.v3.model.Point;
|
||||
import com.google.api.services.monitoring.v3.model.TimeInterval;
|
||||
import com.google.api.services.monitoring.v3.model.TimeSeries;
|
||||
import com.google.api.services.monitoring.v3.model.TypedValue;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.mosapi.MosApiModels.ServiceStatus;
|
||||
import google.registry.mosapi.MosApiModels.TldServiceState;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Stream;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/** Metrics Exporter for MoSAPI. */
|
||||
public class MosApiMetrics {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@Inject
|
||||
public MosApiMetrics() {}
|
||||
// Google Cloud Monitoring Limit: Max 200 TimeSeries per request
|
||||
private static final int MAX_TIMESERIES_PER_REQUEST = 195;
|
||||
|
||||
public void recordStates(List<TldServiceState> states) {
|
||||
// b/467541269: Logic to push status to Cloud Monitoring goes here
|
||||
logger.atInfo().log("MoSAPI record metrics logic will be implemented from here");
|
||||
private static final int METRICS_ALREADY_EXIST = 409;
|
||||
|
||||
// Magic String Constants
|
||||
private static final String METRIC_DOMAIN = "custom.googleapis.com/mosapi/";
|
||||
private static final String PROJECT_RESOURCE_PREFIX = "projects/";
|
||||
private static final String RESOURCE_TYPE_GLOBAL = "global";
|
||||
private static final String LABEL_PROJECT_ID = "project_id";
|
||||
private static final String LABEL_TLD = "tld";
|
||||
private static final String LABEL_SERVICE_TYPE = "service_type";
|
||||
|
||||
// Lock Constants
|
||||
private static final String LOCK_NAME = "MosApiMetricCreation";
|
||||
private static final Duration LOCK_LEASE_TIME = Duration.standardHours(1);
|
||||
// Metric Names
|
||||
private static final String METRIC_TLD_STATUS = "tld_status";
|
||||
private static final String METRIC_SERVICE_STATUS = "service_status";
|
||||
private static final String METRIC_EMERGENCY_USAGE = "emergency_usage";
|
||||
private static final String GAUGE_METRIC_KIND = "GAUGE";
|
||||
|
||||
// Metric Display Names & Descriptions
|
||||
private static final String DISPLAY_NAME_TLD_STATUS =
|
||||
"Health of TLDs. 1 = UP, 0 = DOWN, 2= DISABLED/NOT_MONITORED";
|
||||
private static final String DESC_TLD_STATUS = "Overall Health of TLDs reported from ICANN";
|
||||
|
||||
private static final String DISPLAY_NAME_SERVICE_STATUS =
|
||||
"Health of Services. 1 = UP, 0 = DOWN, 2= DISABLED/NOT_MONITORED";
|
||||
private static final String DESC_SERVICE_STATUS =
|
||||
"Overall Health of Services reported from ICANN";
|
||||
|
||||
private static final String DISPLAY_NAME_EMERGENCY_USAGE =
|
||||
"Percentage of Emergency Threshold Consumed";
|
||||
private static final String DESC_EMERGENCY_USAGE =
|
||||
"Downtime threshold that if reached by any of the monitored Services may cause the TLDs"
|
||||
+ " Services emergency transition to an interim Registry Operator";
|
||||
|
||||
// MoSAPI Status Constants
|
||||
private static final String STATUS_UP_INCONCLUSIVE = "UP-INCONCLUSIVE";
|
||||
private static final String STATUS_DOWN = "DOWN";
|
||||
private static final String STATUS_DISABLED = "DISABLED";
|
||||
|
||||
private final Monitoring monitoringClient;
|
||||
private final String projectId;
|
||||
private final String projectName;
|
||||
private final Clock clock;
|
||||
private final MonitoredResource monitoredResource;
|
||||
private final LockHandler lockHandler;
|
||||
// Flag to ensure we only create descriptors once, lazily
|
||||
@VisibleForTesting static final AtomicBoolean isDescriptorInitialized = new AtomicBoolean(false);
|
||||
|
||||
@Inject
|
||||
public MosApiMetrics(
|
||||
Monitoring monitoringClient,
|
||||
@Config("projectId") String projectId,
|
||||
Clock clock,
|
||||
LockHandler lockHandler) {
|
||||
this.monitoringClient = monitoringClient;
|
||||
this.projectId = projectId;
|
||||
this.clock = clock;
|
||||
this.projectName = PROJECT_RESOURCE_PREFIX + projectId;
|
||||
this.lockHandler = lockHandler;
|
||||
this.monitoredResource =
|
||||
new MonitoredResource()
|
||||
.setType(RESOURCE_TYPE_GLOBAL)
|
||||
.setLabels(ImmutableMap.of(LABEL_PROJECT_ID, projectId));
|
||||
}
|
||||
|
||||
/** Accepts a list of states and processes them in a single async batch task. */
|
||||
public void recordStates(ImmutableList<TldServiceState> states) {
|
||||
// If this is the first time we are recording, ensure descriptors exist.
|
||||
ensureMetricDescriptorsWithLock();
|
||||
pushBatchMetrics(states);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to create metric descriptors using a distributed lock.
|
||||
*
|
||||
* <p>If the lock is acquired, this instance creates the descriptors and marks itself initialized.
|
||||
* If the lock is busy, it implies another instance is handling it, so we skip and proceed.
|
||||
*/
|
||||
private void ensureMetricDescriptorsWithLock() {
|
||||
lockHandler.executeWithLocks(
|
||||
() -> {
|
||||
if (!isDescriptorInitialized.get()) {
|
||||
createCustomMetricDescriptors();
|
||||
isDescriptorInitialized.set(true);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
null,
|
||||
LOCK_LEASE_TIME,
|
||||
LOCK_NAME);
|
||||
}
|
||||
|
||||
// Defines the custom metrics in Cloud Monitoring
|
||||
private void createCustomMetricDescriptors() {
|
||||
// 1. TLD Status Descriptor
|
||||
createMetricDescriptor(
|
||||
METRIC_TLD_STATUS,
|
||||
DISPLAY_NAME_TLD_STATUS,
|
||||
DESC_TLD_STATUS,
|
||||
"INT64",
|
||||
ImmutableList.of(LABEL_TLD));
|
||||
|
||||
// 2. Service Status Descriptor
|
||||
createMetricDescriptor(
|
||||
METRIC_SERVICE_STATUS,
|
||||
DISPLAY_NAME_SERVICE_STATUS,
|
||||
DESC_SERVICE_STATUS,
|
||||
"INT64",
|
||||
ImmutableList.of(LABEL_TLD, LABEL_SERVICE_TYPE));
|
||||
|
||||
// 3. Emergency Usage Descriptor
|
||||
createMetricDescriptor(
|
||||
METRIC_EMERGENCY_USAGE,
|
||||
DISPLAY_NAME_EMERGENCY_USAGE,
|
||||
DESC_EMERGENCY_USAGE,
|
||||
"DOUBLE",
|
||||
ImmutableList.of(LABEL_TLD, LABEL_SERVICE_TYPE));
|
||||
|
||||
logger.atInfo().log("Metric descriptors ensured for project %s", projectId);
|
||||
}
|
||||
|
||||
private void createMetricDescriptor(
|
||||
String metricTypeSuffix,
|
||||
String displayName,
|
||||
String description,
|
||||
String valueType,
|
||||
ImmutableList<String> labelKeys) {
|
||||
|
||||
ImmutableList<LabelDescriptor> labelDescriptors =
|
||||
labelKeys.stream()
|
||||
.map(
|
||||
key ->
|
||||
new LabelDescriptor()
|
||||
.setKey(key)
|
||||
.setValueType("STRING")
|
||||
.setDescription(
|
||||
key.equals(LABEL_TLD)
|
||||
? "The TLD being monitored"
|
||||
: "The type of service"))
|
||||
.collect(toImmutableList());
|
||||
|
||||
MetricDescriptor descriptor =
|
||||
new MetricDescriptor()
|
||||
.setType(METRIC_DOMAIN + metricTypeSuffix)
|
||||
.setMetricKind(GAUGE_METRIC_KIND)
|
||||
.setValueType(valueType)
|
||||
.setDisplayName(displayName)
|
||||
.setDescription(description)
|
||||
.setLabels(labelDescriptors);
|
||||
try {
|
||||
monitoringClient
|
||||
.projects()
|
||||
.metricDescriptors()
|
||||
.create(this.projectName, descriptor)
|
||||
.execute();
|
||||
} catch (GoogleJsonResponseException e) {
|
||||
if (e.getStatusCode() == METRICS_ALREADY_EXIST) {
|
||||
// the metric already exists. This is expected.
|
||||
logger.atFine().log("Metric descriptor %s already exists.", metricTypeSuffix);
|
||||
} else {
|
||||
logger.atWarning().withCause(e).log(
|
||||
"Failed to create metric descriptor %s. Status: %d",
|
||||
metricTypeSuffix, e.getStatusCode());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.atWarning().withCause(e).log(
|
||||
"Unexpected error creating metric descriptor %s.", metricTypeSuffix);
|
||||
}
|
||||
}
|
||||
|
||||
private void pushBatchMetrics(ImmutableList<TldServiceState> states) {
|
||||
Instant now = Instant.ofEpochMilli(clock.nowUtc().getMillis());
|
||||
TimeInterval interval = new TimeInterval().setEndTime(now.toString());
|
||||
Stream<TimeSeries> allTimeSeriesStream =
|
||||
states.stream().flatMap(state -> createMetricsForState(state, interval));
|
||||
|
||||
Iterator<List<TimeSeries>> batchIterator =
|
||||
Iterators.partition(allTimeSeriesStream.iterator(), MAX_TIMESERIES_PER_REQUEST);
|
||||
|
||||
int successCount = 0;
|
||||
int failureCount = 0;
|
||||
|
||||
// Iterate and count
|
||||
while (batchIterator.hasNext()) {
|
||||
List<TimeSeries> batch = batchIterator.next();
|
||||
try {
|
||||
CreateTimeSeriesRequest request = new CreateTimeSeriesRequest().setTimeSeries(batch);
|
||||
monitoringClient.projects().timeSeries().create(this.projectName, request).execute();
|
||||
|
||||
successCount++;
|
||||
|
||||
} catch (IOException e) {
|
||||
failureCount++;
|
||||
// Log individual batch failures, so we have the stack trace for debugging
|
||||
logger.atWarning().withCause(e).log(
|
||||
"Failed to push batch of %d time series.", batch.size());
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Log the final summary
|
||||
if (failureCount > 0) {
|
||||
logger.atWarning().log(
|
||||
"Metric push finished with errors. Batches Succeeded: %d, Failed: %d",
|
||||
successCount, failureCount);
|
||||
} else {
|
||||
logger.atInfo().log("Metric push finished successfully. Batches Succeeded: %d", successCount);
|
||||
}
|
||||
}
|
||||
|
||||
/** Generates all TimeSeries (TLD + Services) for a single state object. */
|
||||
private Stream<TimeSeries> createMetricsForState(TldServiceState state, TimeInterval interval) {
|
||||
// 1. TLD Status
|
||||
Stream<TimeSeries> tldStream = Stream.of(createTldStatusTimeSeries(state, interval));
|
||||
|
||||
// 2. Service Metrics (if any)
|
||||
Stream<TimeSeries> serviceStream =
|
||||
state.serviceStatuses().entrySet().stream()
|
||||
.flatMap(
|
||||
entry ->
|
||||
createServiceMetricsStream(
|
||||
state.tld(), entry.getKey(), entry.getValue(), interval));
|
||||
|
||||
return Stream.concat(tldStream, serviceStream);
|
||||
}
|
||||
|
||||
private Stream<TimeSeries> createServiceMetricsStream(
|
||||
String tld, String serviceType, ServiceStatus statusObj, TimeInterval interval) {
|
||||
ImmutableMap<String, String> labels =
|
||||
ImmutableMap.of(LABEL_TLD, tld, LABEL_SERVICE_TYPE, serviceType);
|
||||
|
||||
return Stream.of(
|
||||
createTimeSeries(
|
||||
METRIC_SERVICE_STATUS, labels, parseServiceStatus(statusObj.status()), interval),
|
||||
createTimeSeries(METRIC_EMERGENCY_USAGE, labels, statusObj.emergencyThreshold(), interval));
|
||||
}
|
||||
|
||||
private TimeSeries createTldStatusTimeSeries(TldServiceState state, TimeInterval interval) {
|
||||
return createTimeSeries(
|
||||
METRIC_TLD_STATUS,
|
||||
ImmutableMap.of(LABEL_TLD, state.tld()),
|
||||
parseTldStatus(state.status()),
|
||||
interval);
|
||||
}
|
||||
|
||||
private TimeSeries createTimeSeries(
|
||||
String suffix, ImmutableMap<String, String> labels, Number val, TimeInterval interval) {
|
||||
Metric metric = new Metric().setType(METRIC_DOMAIN + suffix).setLabels(labels);
|
||||
|
||||
TypedValue tv = new TypedValue();
|
||||
if (val instanceof Double) {
|
||||
tv.setDoubleValue((Double) val);
|
||||
} else {
|
||||
tv.setInt64Value(val.longValue());
|
||||
}
|
||||
|
||||
return new TimeSeries()
|
||||
.setMetric(metric)
|
||||
.setResource(this.monitoredResource)
|
||||
.setPoints(ImmutableList.of(new Point().setInterval(interval).setValue(tv)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates MoSAPI status to a numeric metric.
|
||||
*
|
||||
* <p>Mappings: 1 (UP) = Healthy; 0 (DOWN) = Critical failure; 2 (UP-INCONCLUSIVE) = Disabled/Not
|
||||
* Monitored/In Maintenance.
|
||||
*
|
||||
* <p>A status of 2 indicates the SLA monitoring system is under maintenance. The TLD is
|
||||
* considered "UP" by default, but individual service checks are disabled. This distinguishes
|
||||
* maintenance windows from actual availability or outages.
|
||||
*
|
||||
* @see <a href="https://www.icann.org/mosapi-specification.pdf">ICANN MoSAPI Spec Sec 5.1</a>
|
||||
*/
|
||||
private long parseTldStatus(String status) {
|
||||
return switch (Ascii.toUpperCase(status)) {
|
||||
case STATUS_DOWN -> 0;
|
||||
case STATUS_UP_INCONCLUSIVE -> 2;
|
||||
default -> 1; // status is up
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates MoSAPI service status to a numeric metric.
|
||||
*
|
||||
* <p>Mappings: 1 (UP) = Healthy; 0 (DOWN) = Critical failure; 2 (DISABLED/UP-INCONCLUSIVE*) =
|
||||
* Disabled/Not Monitored/In Maintenance.
|
||||
*
|
||||
* @see <a href="https://www.icann.org/mosapi-specification.pdf">ICANN MoSAPI Spec Sec 5.1</a>
|
||||
*/
|
||||
private long parseServiceStatus(String status) {
|
||||
String serviceStatus = Ascii.toUpperCase(status);
|
||||
if (serviceStatus.startsWith(STATUS_UP_INCONCLUSIVE)) {
|
||||
return 2;
|
||||
}
|
||||
return switch (serviceStatus) {
|
||||
case STATUS_DOWN -> 0;
|
||||
case STATUS_DISABLED -> 2;
|
||||
default -> 1; // status is Up
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,11 +26,9 @@ import google.registry.mosapi.MosApiModels.ServiceStatus;
|
||||
import google.registry.mosapi.MosApiModels.TldServiceState;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** A service that provides business logic for interacting with MoSAPI Service State. */
|
||||
public class MosApiStateService {
|
||||
@@ -135,14 +133,16 @@ public class MosApiStateService {
|
||||
tldExecutor))
|
||||
.collect(toImmutableList());
|
||||
|
||||
List<TldServiceState> allStates =
|
||||
ImmutableList<TldServiceState> allStates =
|
||||
futures.stream()
|
||||
.map(CompletableFuture::join)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
.filter(this::isValidForMetrics)
|
||||
.collect(toImmutableList());
|
||||
|
||||
if (!allStates.isEmpty()) {
|
||||
try {
|
||||
logger.atInfo().log("Triggering MoSAPI status to cloud monitoring for all TLDs.");
|
||||
mosApiMetrics.recordStates(allStates);
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log("Failed to submit MoSAPI metrics batch.");
|
||||
@@ -151,4 +151,14 @@ public class MosApiStateService {
|
||||
logger.atWarning().log("No successful TLD states fetched; skipping metrics push.");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValidForMetrics(TldServiceState state) {
|
||||
if (state.tld() == null || state.status() == null) {
|
||||
logger.atSevere().log(
|
||||
"Contract Violation: Received invalid state (TLD=%s, Status=%s). Skipping.",
|
||||
state.tld(), state.status());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ public class TriggerServiceStateAction implements Runnable {
|
||||
public void run() {
|
||||
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
||||
try {
|
||||
logger.atInfo().log("Beginning to trigger MoSAPI metrics for all TLDs.");
|
||||
stateService.triggerMetricsForAllServiceStateSummaries();
|
||||
response.setStatus(200);
|
||||
response.setPayload("MoSAPI metrics triggered successfully for all TLDs.");
|
||||
|
||||
@@ -200,7 +200,7 @@ public final class MosApiModule {
|
||||
@Singleton
|
||||
@Named("mosapiTldExecutor")
|
||||
static ExecutorService provideMosapiTldExecutor(
|
||||
@Config("mosapiTldThreadCnt") int threadPoolSize) {
|
||||
@Config("mosapiTldThreadCount") int threadPoolSize) {
|
||||
return Executors.newFixedThreadPool(threadPoolSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,28 +14,12 @@
|
||||
|
||||
package google.registry.privileges.secretmanager;
|
||||
|
||||
import com.google.cloud.secretmanager.v1.SecretVersionName;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.privileges.secretmanager.SecretManagerClient.NoSuchSecretResourceException;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Storage of SQL users' login credentials, backed by Cloud Secret Manager.
|
||||
*
|
||||
* <p>A user's credential is stored with one level of indirection using two secret IDs: Each version
|
||||
* of the <em>credential data</em> is stored as follows: its secret ID is determined by {@link
|
||||
* #getCredentialDataSecretId(SqlUser, String dbInstance)}, and the value of each version is a
|
||||
* {@link SqlCredential}, serialized using {@link SqlCredential#toFormattedString}. The 'live'
|
||||
* version of the credential is saved under the 'live pointer' secret explained below.
|
||||
*
|
||||
* <p>The pointer to the 'live' version of the credential data is stored as follows: its secret ID
|
||||
* is determined by {@link #getLiveLabelSecretId(SqlUser, String dbInstance)}; and the value of each
|
||||
* version is a {@link SecretVersionName} in String form, pointing to a version of the credential
|
||||
* data. Only the 'latest' version of this secret should be used. It is guaranteed to be valid.
|
||||
*
|
||||
* <p>The indirection in credential storage makes it easy to handle failures in the credential
|
||||
* change process.
|
||||
*/
|
||||
public class SqlCredentialStore {
|
||||
private final SecretManagerClient csmClient;
|
||||
@@ -49,61 +33,19 @@ public class SqlCredentialStore {
|
||||
}
|
||||
|
||||
public SqlCredential getCredential(SqlUser user) {
|
||||
SecretVersionName credentialName = getLiveCredentialSecretVersion(user);
|
||||
return SqlCredential.fromFormattedString(
|
||||
csmClient.getSecretData(
|
||||
credentialName.getSecret(), Optional.of(credentialName.getSecretVersion())));
|
||||
var secretId = getSecretIdForUserPassword(user);
|
||||
var secretData = csmClient.getSecretData(secretId, Optional.empty());
|
||||
return SqlCredential.fromFormattedString(secretData);
|
||||
}
|
||||
|
||||
public void createOrUpdateCredential(SqlUser user, String password) {
|
||||
SecretVersionName dataName = saveCredentialData(user, password);
|
||||
saveLiveLabel(user, dataName);
|
||||
var secretId = getSecretIdForUserPassword(user);
|
||||
csmClient.createSecretIfAbsent(secretId);
|
||||
csmClient.addSecretVersion(
|
||||
secretId, SqlCredential.create(user.geUserName(), password).toFormattedString());
|
||||
}
|
||||
|
||||
public void deleteCredential(SqlUser user) {
|
||||
try {
|
||||
csmClient.deleteSecret(getCredentialDataSecretId(user, dbInstance));
|
||||
} catch (NoSuchSecretResourceException e) {
|
||||
// ok
|
||||
}
|
||||
try {
|
||||
csmClient.deleteSecret(getLiveLabelSecretId(user, dbInstance));
|
||||
} catch (NoSuchSecretResourceException e) {
|
||||
// ok.
|
||||
}
|
||||
}
|
||||
|
||||
private SecretVersionName saveCredentialData(SqlUser user, String password) {
|
||||
String credentialDataSecretId = getCredentialDataSecretId(user, dbInstance);
|
||||
csmClient.createSecretIfAbsent(credentialDataSecretId);
|
||||
String credentialVersion =
|
||||
csmClient.addSecretVersion(
|
||||
credentialDataSecretId,
|
||||
SqlCredential.create(createDatabaseLoginName(user), password).toFormattedString());
|
||||
return SecretVersionName.of(csmClient.getProject(), credentialDataSecretId, credentialVersion);
|
||||
}
|
||||
|
||||
private void saveLiveLabel(SqlUser user, SecretVersionName dataVersionName) {
|
||||
String liveLabelSecretId = getLiveLabelSecretId(user, dbInstance);
|
||||
csmClient.createSecretIfAbsent(liveLabelSecretId);
|
||||
csmClient.addSecretVersion(liveLabelSecretId, dataVersionName.toString());
|
||||
}
|
||||
|
||||
private SecretVersionName getLiveCredentialSecretVersion(SqlUser user) {
|
||||
return SecretVersionName.parse(
|
||||
csmClient.getSecretData(getLiveLabelSecretId(user, dbInstance), Optional.empty()));
|
||||
}
|
||||
|
||||
private static String getLiveLabelSecretId(SqlUser user, String dbInstance) {
|
||||
return String.format("sql-cred-live-label-%s-%s", user.geUserName(), dbInstance);
|
||||
}
|
||||
|
||||
private static String getCredentialDataSecretId(SqlUser user, String dbInstance) {
|
||||
return String.format("sql-cred-data-%s-%s", user.geUserName(), dbInstance);
|
||||
}
|
||||
|
||||
// WIP: when b/170230882 is complete, login will be versioned.
|
||||
private static String createDatabaseLoginName(SqlUser user) {
|
||||
return user.geUserName();
|
||||
private String getSecretIdForUserPassword(SqlUser user) {
|
||||
return String.format("sql-password-for-%s-on-%s", user.geUserName(), this.dbInstance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.rde;
|
||||
|
||||
import static google.registry.util.XmlEnumUtils.enumToXml;
|
||||
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactAddress;
|
||||
import google.registry.model.contact.ContactPhoneNumber;
|
||||
import google.registry.model.contact.Disclose;
|
||||
import google.registry.model.contact.Disclose.PostalInfoChoice;
|
||||
import google.registry.model.contact.PostalInfo;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.xjc.contact.XjcContactAddrType;
|
||||
import google.registry.xjc.contact.XjcContactDiscloseType;
|
||||
import google.registry.xjc.contact.XjcContactE164Type;
|
||||
import google.registry.xjc.contact.XjcContactIntLocType;
|
||||
import google.registry.xjc.contact.XjcContactPostalInfoEnumType;
|
||||
import google.registry.xjc.contact.XjcContactPostalInfoType;
|
||||
import google.registry.xjc.contact.XjcContactStatusType;
|
||||
import google.registry.xjc.contact.XjcContactStatusValueType;
|
||||
import google.registry.xjc.eppcom.XjcEppcomTrStatusType;
|
||||
import google.registry.xjc.rdecontact.XjcRdeContact;
|
||||
import google.registry.xjc.rdecontact.XjcRdeContactElement;
|
||||
import google.registry.xjc.rdecontact.XjcRdeContactTransferDataType;
|
||||
import javax.annotation.CheckForNull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Utility class that turns {@link Contact} as {@link XjcRdeContactElement}. */
|
||||
final class ContactToXjcConverter {
|
||||
|
||||
/** Converts {@link Contact} to {@link XjcRdeContactElement}. */
|
||||
static XjcRdeContactElement convert(Contact host) {
|
||||
return new XjcRdeContactElement(convertContact(host));
|
||||
}
|
||||
|
||||
/** Converts {@link Contact} to {@link XjcRdeContact}. */
|
||||
static XjcRdeContact convertContact(Contact model) {
|
||||
XjcRdeContact bean = new XjcRdeContact();
|
||||
bean.setRoid(model.getRepoId());
|
||||
for (StatusValue status : model.getStatusValues()) {
|
||||
bean.getStatuses().add(convertStatusValue(status));
|
||||
}
|
||||
PostalInfo localizedPostalInfo = model.getLocalizedPostalInfo();
|
||||
if (localizedPostalInfo != null) {
|
||||
bean.getPostalInfos().add(convertPostalInfo(localizedPostalInfo));
|
||||
}
|
||||
PostalInfo internationalizedPostalInfo = model.getInternationalizedPostalInfo();
|
||||
if (internationalizedPostalInfo != null) {
|
||||
bean.getPostalInfos().add(convertPostalInfo(internationalizedPostalInfo));
|
||||
}
|
||||
bean.setId(model.getContactId());
|
||||
bean.setClID(model.getCurrentSponsorRegistrarId());
|
||||
bean.setCrRr(RdeAdapter.convertRr(model.getCreationRegistrarId(), null));
|
||||
bean.setUpRr(RdeAdapter.convertRr(model.getLastEppUpdateRegistrarId(), null));
|
||||
bean.setCrDate(model.getCreationTime());
|
||||
bean.setUpDate(model.getLastEppUpdateTime());
|
||||
bean.setTrDate(model.getLastTransferTime());
|
||||
bean.setVoice(convertPhoneNumber(model.getVoiceNumber()));
|
||||
bean.setFax(convertPhoneNumber(model.getFaxNumber()));
|
||||
bean.setEmail(model.getEmailAddress());
|
||||
bean.setDisclose(convertDisclose(model.getDisclose()));
|
||||
|
||||
// o An OPTIONAL <trnData> element that contains the following child
|
||||
// elements related to the last transfer request of the contact
|
||||
// object:
|
||||
//
|
||||
// * A <trStatus> element that contains the state of the most recent
|
||||
// transfer request.
|
||||
//
|
||||
// * A <reRr> element that contains the identifier of the registrar
|
||||
// that requested the domain name object transfer. An OPTIONAL
|
||||
// client attribute is used to specify the client that performed
|
||||
// the operation.
|
||||
//
|
||||
// * An <acRr> element that contains the identifier of the registrar
|
||||
// that SHOULD act upon a PENDING transfer request. For all other
|
||||
// status types, the value identifies the registrar that took the
|
||||
// indicated action. An OPTIONAL client attribute is used to
|
||||
// specify the client that performed the operation.
|
||||
//
|
||||
// * A <reDate> element that contains the date and time that the
|
||||
// transfer was requested.
|
||||
//
|
||||
// * An <acDate> element that contains the date and time of a
|
||||
// required or completed response. For a PENDING request, the
|
||||
// value identifies the date and time by which a response is
|
||||
// required before an automated response action will be taken by
|
||||
// the registry. For all other status types, the value identifies
|
||||
// the date and time when the request was completed.
|
||||
if (!model.getTransferData().isEmpty()) {
|
||||
bean.setTrnData(convertTransferData(model.getTransferData()));
|
||||
}
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
/** Converts {@link TransferData} to {@link XjcRdeContactTransferDataType}. */
|
||||
private static XjcRdeContactTransferDataType convertTransferData(TransferData model) {
|
||||
XjcRdeContactTransferDataType bean = new XjcRdeContactTransferDataType();
|
||||
bean.setTrStatus(XjcEppcomTrStatusType.fromValue(model.getTransferStatus().getXmlName()));
|
||||
bean.setReRr(RdeUtils.makeXjcRdeRrType(model.getGainingRegistrarId()));
|
||||
bean.setAcRr(RdeUtils.makeXjcRdeRrType(model.getLosingRegistrarId()));
|
||||
bean.setReDate(model.getTransferRequestTime());
|
||||
bean.setAcDate(model.getPendingTransferExpirationTime());
|
||||
return bean;
|
||||
}
|
||||
|
||||
/** Converts {@link ContactAddress} to {@link XjcContactAddrType}. */
|
||||
private static XjcContactAddrType convertAddress(ContactAddress model) {
|
||||
XjcContactAddrType bean = new XjcContactAddrType();
|
||||
bean.getStreets().addAll(model.getStreet());
|
||||
bean.setCity(model.getCity());
|
||||
bean.setSp(model.getState());
|
||||
bean.setPc(model.getZip());
|
||||
bean.setCc(model.getCountryCode());
|
||||
return bean;
|
||||
}
|
||||
|
||||
/** Converts {@link Disclose} to {@link XjcContactDiscloseType}. */
|
||||
@Nullable
|
||||
@CheckForNull
|
||||
static XjcContactDiscloseType convertDisclose(@Nullable Disclose model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
XjcContactDiscloseType bean = new XjcContactDiscloseType();
|
||||
bean.setFlag(model.getFlag());
|
||||
for (PostalInfoChoice loc : model.getNames()) {
|
||||
bean.getNames().add(convertPostalInfoChoice(loc));
|
||||
}
|
||||
for (PostalInfoChoice loc : model.getOrgs()) {
|
||||
bean.getOrgs().add(convertPostalInfoChoice(loc));
|
||||
}
|
||||
for (PostalInfoChoice loc : model.getAddrs()) {
|
||||
bean.getAddrs().add(convertPostalInfoChoice(loc));
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
/** Converts {@link ContactPhoneNumber} to {@link XjcContactE164Type}. */
|
||||
@Nullable
|
||||
@CheckForNull
|
||||
private static XjcContactE164Type convertPhoneNumber(@Nullable ContactPhoneNumber model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
XjcContactE164Type bean = new XjcContactE164Type();
|
||||
bean.setValue(model.getPhoneNumber());
|
||||
bean.setX(model.getExtension());
|
||||
return bean;
|
||||
}
|
||||
|
||||
/** Converts {@link PostalInfoChoice} to {@link XjcContactIntLocType}. */
|
||||
private static XjcContactIntLocType convertPostalInfoChoice(PostalInfoChoice model) {
|
||||
XjcContactIntLocType bean = new XjcContactIntLocType();
|
||||
bean.setType(XjcContactPostalInfoEnumType.fromValue(enumToXml(model.getType())));
|
||||
return bean;
|
||||
}
|
||||
|
||||
/** Converts {@link PostalInfo} to {@link XjcContactPostalInfoType}. */
|
||||
private static XjcContactPostalInfoType convertPostalInfo(PostalInfo model) {
|
||||
XjcContactPostalInfoType bean = new XjcContactPostalInfoType();
|
||||
bean.setName(model.getName());
|
||||
bean.setOrg(model.getOrg());
|
||||
bean.setAddr(convertAddress(model.getAddress()));
|
||||
bean.setType(XjcContactPostalInfoEnumType.fromValue(enumToXml(model.getType())));
|
||||
return bean;
|
||||
}
|
||||
|
||||
/** Converts {@link StatusValue} to {@link XjcContactStatusType}. */
|
||||
private static XjcContactStatusType convertStatusValue(StatusValue model) {
|
||||
XjcContactStatusType bean = new XjcContactStatusType();
|
||||
bean.setS(XjcContactStatusValueType.fromValue(model.getXmlName()));
|
||||
return bean;
|
||||
}
|
||||
|
||||
private ContactToXjcConverter() {}
|
||||
}
|
||||
@@ -14,14 +14,9 @@
|
||||
|
||||
package google.registry.rde;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.secdns.DomainDsData;
|
||||
@@ -29,10 +24,7 @@ import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.rde.RdeMode;
|
||||
import google.registry.model.transfer.DomainTransferData;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.Idn;
|
||||
import google.registry.xjc.domain.XjcDomainContactAttrType;
|
||||
import google.registry.xjc.domain.XjcDomainContactType;
|
||||
import google.registry.xjc.domain.XjcDomainNsType;
|
||||
import google.registry.xjc.domain.XjcDomainStatusType;
|
||||
import google.registry.xjc.domain.XjcDomainStatusValueType;
|
||||
@@ -44,7 +36,6 @@ import google.registry.xjc.rgp.XjcRgpStatusType;
|
||||
import google.registry.xjc.rgp.XjcRgpStatusValueType;
|
||||
import google.registry.xjc.secdns.XjcSecdnsDsDataType;
|
||||
import google.registry.xjc.secdns.XjcSecdnsDsOrKeyType;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Utility class that turns {@link Domain} as {@link XjcRdeDomainElement}. */
|
||||
final class DomainToXjcConverter {
|
||||
@@ -152,8 +143,6 @@ final class DomainToXjcConverter {
|
||||
|
||||
switch (mode) {
|
||||
case FULL:
|
||||
String domainName = model.getDomainName();
|
||||
|
||||
// o Zero or more OPTIONAL <rgpStatus> element to represent
|
||||
// "pendingDelete" sub-statuses, including "redemptionPeriod",
|
||||
// "pendingRestore", and "pendingDelete", that a domain name can be
|
||||
@@ -163,25 +152,6 @@ final class DomainToXjcConverter {
|
||||
bean.getRgpStatuses().add(convertGracePeriodStatus(status));
|
||||
}
|
||||
|
||||
// o An OPTIONAL <registrant> element that contain the identifier for
|
||||
// the human or organizational social information object associated
|
||||
// as the holder of the domain name object.
|
||||
Optional<VKey<Contact>> registrant = model.getRegistrant();
|
||||
if (registrant.isPresent()) {
|
||||
Optional<Contact> registrantContact =
|
||||
tm().transact(() -> tm().loadByKeyIfPresent(registrant.get()));
|
||||
registrantContact.ifPresent(c -> bean.setRegistrant(c.getContactId()));
|
||||
}
|
||||
|
||||
// o Zero or more OPTIONAL <contact> elements that contain identifiers
|
||||
// for the human or organizational social information objects
|
||||
// associated with the domain name object.
|
||||
for (DesignatedContact contact : model.getContacts()) {
|
||||
Optional<XjcDomainContactType> contactType =
|
||||
convertDesignatedContact(contact, domainName);
|
||||
contactType.ifPresent(c -> bean.getContacts().add(c));
|
||||
}
|
||||
|
||||
// o An OPTIONAL <secDNS> element that contains the public key
|
||||
// information associated with Domain Name System security (DNSSEC)
|
||||
// extensions for the domain name as specified in [RFC5910].
|
||||
@@ -289,23 +259,5 @@ final class DomainToXjcConverter {
|
||||
return bean;
|
||||
}
|
||||
|
||||
/** Converts {@link DesignatedContact} to {@link XjcDomainContactType}. */
|
||||
private static Optional<XjcDomainContactType> convertDesignatedContact(
|
||||
DesignatedContact model, String domainName) {
|
||||
XjcDomainContactType bean = new XjcDomainContactType();
|
||||
checkState(
|
||||
model.getContactKey() != null,
|
||||
"Contact key for type %s is null on domain %s",
|
||||
model.getType(),
|
||||
domainName);
|
||||
Optional<Contact> contact = tm().transact(() -> tm().loadByKeyIfPresent(model.getContactKey()));
|
||||
if (contact.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
bean.setType(XjcDomainContactAttrType.fromValue(Ascii.toLowerCase(model.getType().toString())));
|
||||
bean.setValue(contact.get().getContactId());
|
||||
return Optional.of(bean);
|
||||
}
|
||||
|
||||
private DomainToXjcConverter() {}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.rde.RdeMode;
|
||||
@@ -118,12 +117,6 @@ public final class RdeMarshaller implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/** Turns {@link Contact} object into an XML fragment. */
|
||||
public DepositFragment marshalContact(Contact contact) {
|
||||
return marshalResource(
|
||||
RdeResourceType.CONTACT, contact, ContactToXjcConverter.convert(contact));
|
||||
}
|
||||
|
||||
/** Turns {@link Domain} object into an XML fragment. */
|
||||
public DepositFragment marshalDomain(Domain domain, RdeMode mode) {
|
||||
return marshalResource(
|
||||
|
||||
@@ -25,7 +25,6 @@ import java.util.EnumSet;
|
||||
|
||||
/** Types of objects that get embedded in an escrow deposit. */
|
||||
public enum RdeResourceType {
|
||||
CONTACT("urn:ietf:params:xml:ns:rdeContact-1.0", EnumSet.of(FULL)),
|
||||
DOMAIN("urn:ietf:params:xml:ns:rdeDomain-1.0", EnumSet.of(FULL, THIN)),
|
||||
HOST("urn:ietf:params:xml:ns:rdeHost-1.0", EnumSet.of(FULL)),
|
||||
REGISTRAR("urn:ietf:params:xml:ns:rdeRegistrar-1.0", EnumSet.of(FULL, THIN)),
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
import google.registry.tools.params.PhoneNumberParameter;
|
||||
import google.registry.tools.soy.ContactCreateSoyInfo;
|
||||
import google.registry.util.StringGenerator;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.util.List;
|
||||
|
||||
/** A command to create a new contact via EPP. */
|
||||
@Parameters(separators = " =", commandDescription = "Create a new contact via EPP.")
|
||||
final class CreateContactCommand extends MutatingEppToolCommand {
|
||||
|
||||
// TODO(b/19016175): Expand to allow full suite of contact flows.
|
||||
@Parameter(
|
||||
names = {"-c", "--client"},
|
||||
description = "Client identifier of the registrar to execute the command as",
|
||||
required = true)
|
||||
String clientId;
|
||||
|
||||
@Parameter(
|
||||
names = {"-i", "--id"},
|
||||
description = "Contact ID.")
|
||||
private String id;
|
||||
|
||||
@Parameter(
|
||||
names = {"-n", "--name"},
|
||||
description = "Contact name.")
|
||||
private String name;
|
||||
|
||||
@Parameter(
|
||||
names = {"-o", "--org"},
|
||||
description = "Organization")
|
||||
private String org;
|
||||
|
||||
@Parameter(
|
||||
names = "--street",
|
||||
description = "Street lines of address. Can take up to 3 lines.",
|
||||
variableArity = true)
|
||||
private List<String> street;
|
||||
|
||||
@Parameter(
|
||||
names = "--city",
|
||||
description = "City of address.")
|
||||
private String city;
|
||||
|
||||
@Parameter(
|
||||
names = "--state",
|
||||
description = "State of address.")
|
||||
private String state;
|
||||
|
||||
@Parameter(
|
||||
names = {"-z", "--zip"},
|
||||
description = "Postal code of address.")
|
||||
private String zip;
|
||||
|
||||
@Parameter(
|
||||
names = "--cc",
|
||||
description = "Country code of address.")
|
||||
private String cc;
|
||||
|
||||
@Parameter(
|
||||
names = "--phone",
|
||||
description = "E.164 phone number, e.g. +1.2125650666",
|
||||
converter = PhoneNumberParameter.class,
|
||||
validateWith = PhoneNumberParameter.class)
|
||||
String phone;
|
||||
|
||||
@Parameter(
|
||||
names = "--fax",
|
||||
description = "E.164 fax number, e.g. +1.2125650666",
|
||||
converter = PhoneNumberParameter.class,
|
||||
validateWith = PhoneNumberParameter.class)
|
||||
String fax;
|
||||
|
||||
@Parameter(
|
||||
names = {"-e", "--email"},
|
||||
description = "Email address.")
|
||||
private String email;
|
||||
|
||||
@Parameter(
|
||||
names = {"-p", "--password"},
|
||||
description = "Password. Optional, randomly generated if not provided.")
|
||||
private String password;
|
||||
|
||||
@Inject
|
||||
@Named("base64StringGenerator")
|
||||
StringGenerator passwordGenerator;
|
||||
|
||||
private static final int PASSWORD_LENGTH = 16;
|
||||
|
||||
@Override
|
||||
protected void initMutatingEppToolCommand() {
|
||||
if (isNullOrEmpty(password)) {
|
||||
password = passwordGenerator.createString(PASSWORD_LENGTH);
|
||||
}
|
||||
checkArgument(street == null || street.size() <= 3,
|
||||
"Addresses must contain at most 3 street lines.");
|
||||
|
||||
setSoyTemplate(ContactCreateSoyInfo.getInstance(), ContactCreateSoyInfo.CONTACTCREATE);
|
||||
addSoyRecord(clientId, new SoyMapData(
|
||||
"id", id,
|
||||
"name", name,
|
||||
"org", org,
|
||||
"street", street,
|
||||
"city", city,
|
||||
"state", state,
|
||||
"zip", zip,
|
||||
"cc", cc,
|
||||
"phone", phone,
|
||||
"fax", fax,
|
||||
"email", email,
|
||||
"password", password));
|
||||
}
|
||||
}
|
||||
@@ -85,9 +85,6 @@ final class CreateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
"domain", domain,
|
||||
"period", period,
|
||||
"nameservers", nameservers,
|
||||
"registrant", registrant,
|
||||
"admins", admins,
|
||||
"techs", techs,
|
||||
"password", password,
|
||||
"currency", currency,
|
||||
"price", cost,
|
||||
|
||||
@@ -21,7 +21,6 @@ import com.beust.jcommander.Parameter;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.tools.params.NameserversParameter;
|
||||
import google.registry.tools.params.StringListParameter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -47,24 +46,6 @@ abstract class CreateOrUpdateDomainCommand extends MutatingEppToolCommand {
|
||||
validateWith = NameserversParameter.class)
|
||||
Set<String> nameservers = new HashSet<>();
|
||||
|
||||
@Parameter(
|
||||
names = {"-r", "--registrant"},
|
||||
description = "Domain registrant."
|
||||
)
|
||||
String registrant;
|
||||
|
||||
@Parameter(
|
||||
names = {"-a", "--admins"},
|
||||
description = "Comma-separated list of admin contacts.",
|
||||
listConverter = StringListParameter.class)
|
||||
List<String> admins = new ArrayList<>();
|
||||
|
||||
@Parameter(
|
||||
names = {"-t", "--techs"},
|
||||
description = "Comma-separated list of technical contacts.",
|
||||
listConverter = StringListParameter.class)
|
||||
List<String> techs = new ArrayList<>();
|
||||
|
||||
@Parameter(
|
||||
names = {"-p", "--password"},
|
||||
description = "Password."
|
||||
|
||||
@@ -400,7 +400,10 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
|
||||
if (registrarName != null && !registrarName.equals(oldRegistrarName)) {
|
||||
String normalizedName = normalizeRegistrarName(registrarName);
|
||||
for (Registrar registrar : Registrar.loadAll()) {
|
||||
if (registrar.getRegistrarName() != null) {
|
||||
// Only check against other registrars (i.e. not the existing version of this one), and
|
||||
// which also have a display name set.
|
||||
if (!registrar.getRegistrarId().equals(clientId)
|
||||
&& registrar.getRegistrarName() != null) {
|
||||
checkArgument(
|
||||
!normalizedName.equals(normalizeRegistrarName(registrar.getRegistrarName())),
|
||||
"The registrar name %s normalizes identically to existing registrar name %s",
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.contact.Contact;
|
||||
import java.util.List;
|
||||
|
||||
/** Command to show one or more contacts. */
|
||||
@Parameters(separators = " =", commandDescription = "Show contact resource(s)")
|
||||
final class GetContactCommand extends GetEppResourceCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "Contact id(s)",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Override
|
||||
public void runAndPrint() {
|
||||
for (String contactId : mainParameters) {
|
||||
printResource(
|
||||
"Contact",
|
||||
contactId,
|
||||
ForeignKeyUtils.loadResource(Contact.class, contactId, readTimestamp));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,6 @@ public final class RegistryTool {
|
||||
"create_cancellations_for_billing_events",
|
||||
CreateCancellationsForBillingEventsCommand.class)
|
||||
.put("create_cdns_tld", CreateCdnsTld.class)
|
||||
.put("create_contact", CreateContactCommand.class)
|
||||
.put("create_domain", CreateDomainCommand.class)
|
||||
.put("create_host", CreateHostCommand.class)
|
||||
.put("create_premium_list", CreatePremiumListCommand.class)
|
||||
@@ -72,7 +71,6 @@ public final class RegistryTool {
|
||||
.put("get_allocation_token", GetAllocationTokenCommand.class)
|
||||
.put("get_bulk_pricing_package", GetBulkPricingPackageCommand.class)
|
||||
.put("get_claims_list", GetClaimsListCommand.class)
|
||||
.put("get_contact", GetContactCommand.class)
|
||||
.put("get_domain", GetDomainCommand.class)
|
||||
.put("get_feature_flag", GetFeatureFlagCommand.class)
|
||||
.put("get_history_entries", GetHistoryEntriesCommand.class)
|
||||
|
||||
@@ -83,8 +83,6 @@ interface RegistryToolComponent {
|
||||
|
||||
void inject(CreateCdnsTld command);
|
||||
|
||||
void inject(CreateContactCommand command);
|
||||
|
||||
void inject(CreateDomainCommand command);
|
||||
|
||||
void inject(CreateRegistrarCommand command);
|
||||
@@ -107,8 +105,6 @@ interface RegistryToolComponent {
|
||||
|
||||
void inject(GetBulkPricingPackageCommand command);
|
||||
|
||||
void inject(GetContactCommand command);
|
||||
|
||||
void inject(GetDomainCommand command);
|
||||
|
||||
void inject(GetFeatureFlagCommand command);
|
||||
|
||||
@@ -15,11 +15,9 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.model.domain.rgp.GracePeriodStatus.AUTO_RENEW;
|
||||
import static google.registry.model.eppcommon.StatusValue.PENDING_DELETE;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static java.util.function.Predicate.isEqual;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
@@ -30,7 +28,6 @@ import com.google.common.collect.Sets;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
import google.registry.flows.ResourceFlowUtils;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.GracePeriodBase;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
@@ -66,15 +63,6 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
validateWith = NameserversParameter.class)
|
||||
private Set<String> addNameservers = new HashSet<>();
|
||||
|
||||
// TODO(b/184067241): enforce only one of each type of contact
|
||||
@Parameter(
|
||||
names = "--add_admins",
|
||||
description = "Admins to add. Cannot be set if --admins is set.")
|
||||
private List<String> addAdmins = new ArrayList<>();
|
||||
|
||||
@Parameter(names = "--add_techs", description = "Techs to add. Cannot be set if --techs is set.")
|
||||
private List<String> addTechs = new ArrayList<>();
|
||||
|
||||
@Parameter(
|
||||
names = "--add_statuses",
|
||||
description = "Statuses to add. Cannot be set if --statuses is set."
|
||||
@@ -97,18 +85,6 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
validateWith = NameserversParameter.class)
|
||||
private Set<String> removeNameservers = new HashSet<>();
|
||||
|
||||
@Parameter(
|
||||
names = "--remove_admins",
|
||||
description = "Admins to remove. Cannot be set if --admins is set."
|
||||
)
|
||||
private List<String> removeAdmins = new ArrayList<>();
|
||||
|
||||
@Parameter(
|
||||
names = "--remove_techs",
|
||||
description = "Techs to remove. Cannot be set if --techs is set."
|
||||
)
|
||||
private List<String> removeTechs = new ArrayList<>();
|
||||
|
||||
@Parameter(
|
||||
names = "--remove_statuses",
|
||||
description = "Statuses to remove. Cannot be set if --statuses is set."
|
||||
@@ -154,16 +130,6 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
} else {
|
||||
checkArgument(addNameservers.size() <= 13, "You can add at most 13 nameservers.");
|
||||
}
|
||||
if (!admins.isEmpty()) {
|
||||
checkArgument(
|
||||
addAdmins.isEmpty() && removeAdmins.isEmpty(),
|
||||
"If you provide the admins flag, you cannot use the add_admins and remove_admins flags.");
|
||||
}
|
||||
if (!techs.isEmpty()) {
|
||||
checkArgument(
|
||||
addTechs.isEmpty() && removeTechs.isEmpty(),
|
||||
"If you provide the techs flag, you cannot use the add_techs and remove_techs flags.");
|
||||
}
|
||||
if (!statuses.isEmpty()) {
|
||||
checkArgument(
|
||||
addStatuses.isEmpty() && removeStatuses.isEmpty(),
|
||||
@@ -197,16 +163,12 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
domainName);
|
||||
|
||||
// Use TreeSets so that the results are always in the same order (this makes testing easier).
|
||||
Set<String> addAdminsThisDomain = new TreeSet<>(addAdmins);
|
||||
Set<String> removeAdminsThisDomain = new TreeSet<>(removeAdmins);
|
||||
Set<String> addTechsThisDomain = new TreeSet<>(addTechs);
|
||||
Set<String> removeTechsThisDomain = new TreeSet<>(removeTechs);
|
||||
Set<String> addNameserversThisDomain = new TreeSet<>(addNameservers);
|
||||
Set<String> removeNameserversThisDomain = new TreeSet<>(removeNameservers);
|
||||
Set<String> addStatusesThisDomain = new TreeSet<>(addStatuses);
|
||||
Set<String> removeStatusesThisDomain = new TreeSet<>(removeStatuses);
|
||||
|
||||
if (!nameservers.isEmpty() || !admins.isEmpty() || !techs.isEmpty() || !statuses.isEmpty()) {
|
||||
if (!nameservers.isEmpty() || !statuses.isEmpty()) {
|
||||
if (!nameservers.isEmpty()) {
|
||||
ImmutableSortedSet<String> existingNameservers = domain.loadNameserverHostNames();
|
||||
populateAddRemoveLists(
|
||||
@@ -223,27 +185,7 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
"The resulting nameservers count for domain %s would be more than 13",
|
||||
domainName);
|
||||
}
|
||||
if (!admins.isEmpty() || !techs.isEmpty()) {
|
||||
ImmutableSet<String> existingAdmins =
|
||||
getContactsOfType(domain, DesignatedContact.Type.ADMIN);
|
||||
ImmutableSet<String> existingTechs =
|
||||
getContactsOfType(domain, DesignatedContact.Type.TECH);
|
||||
|
||||
if (!admins.isEmpty()) {
|
||||
populateAddRemoveLists(
|
||||
ImmutableSet.copyOf(admins),
|
||||
existingAdmins,
|
||||
addAdminsThisDomain,
|
||||
removeAdminsThisDomain);
|
||||
}
|
||||
if (!techs.isEmpty()) {
|
||||
populateAddRemoveLists(
|
||||
ImmutableSet.copyOf(techs),
|
||||
existingTechs,
|
||||
addTechsThisDomain,
|
||||
removeTechsThisDomain);
|
||||
}
|
||||
}
|
||||
if (!statuses.isEmpty()) {
|
||||
Set<String> currentStatusValues = new HashSet<>();
|
||||
for (StatusValue statusValue : domain.getStatusValues()) {
|
||||
@@ -259,17 +201,13 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
|
||||
boolean add =
|
||||
(!addNameserversThisDomain.isEmpty()
|
||||
|| !addAdminsThisDomain.isEmpty()
|
||||
|| !addTechsThisDomain.isEmpty()
|
||||
|| !addStatusesThisDomain.isEmpty());
|
||||
|
||||
boolean remove =
|
||||
(!removeNameserversThisDomain.isEmpty()
|
||||
|| !removeAdminsThisDomain.isEmpty()
|
||||
|| !removeTechsThisDomain.isEmpty()
|
||||
|| !removeStatusesThisDomain.isEmpty());
|
||||
|
||||
boolean change = (registrant != null || password != null);
|
||||
boolean change = password != null;
|
||||
boolean secDns =
|
||||
(!addDsRecords.isEmpty()
|
||||
|| !removeDsRecords.isEmpty()
|
||||
@@ -297,16 +235,11 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
"domain", domainName,
|
||||
"add", add,
|
||||
"addNameservers", addNameserversThisDomain,
|
||||
"addAdmins", addAdminsThisDomain,
|
||||
"addTechs", addTechsThisDomain,
|
||||
"addStatuses", addStatusesThisDomain,
|
||||
"remove", remove,
|
||||
"removeNameservers", removeNameserversThisDomain,
|
||||
"removeAdmins", removeAdminsThisDomain,
|
||||
"removeTechs", removeTechsThisDomain,
|
||||
"removeStatuses", removeStatusesThisDomain,
|
||||
"change", change,
|
||||
"registrant", registrant,
|
||||
"password", password,
|
||||
"secdns", secDns,
|
||||
"addDsRecords", DsRecord.convertToSoy(addDsRecords),
|
||||
@@ -337,13 +270,4 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
addSet.addAll(Sets.difference(targetSet, oldSet));
|
||||
removeSet.addAll(Sets.difference(oldSet, targetSet));
|
||||
}
|
||||
|
||||
ImmutableSet<String> getContactsOfType(Domain domain, final DesignatedContact.Type contactType) {
|
||||
return tm().transact(
|
||||
() ->
|
||||
domain.getContacts().stream()
|
||||
.filter(contact -> contact.getType().equals(contactType))
|
||||
.map(contact -> tm().loadByKey(contact.getContactKey()).getContactId())
|
||||
.collect(toImmutableSet()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.tools.params;
|
||||
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
|
||||
/** Enum to make it easy for a command to accept a flag that specifies an EppResource subclass. */
|
||||
public enum EppResourceTypeParameter {
|
||||
CONTACT(Contact.class),
|
||||
DOMAIN(Domain.class),
|
||||
HOST(Host.class);
|
||||
|
||||
private final Class<? extends EppResource> type;
|
||||
|
||||
EppResourceTypeParameter(Class<? extends EppResource> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Class<? extends EppResource> getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
{
|
||||
"name": "Wipe Out PII From Old Contact History Entries",
|
||||
"description": "An Apache Beam batch pipeline that finds old contact history entries and remove PII information from them.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "registryEnvironment",
|
||||
"label": "The Registry environment.",
|
||||
"helpText": "The Registry environment.",
|
||||
"is_optional": false,
|
||||
"regexes": [
|
||||
"^PRODUCTION|SANDBOX|CRASH|QA|ALPHA$"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "isolationOverride",
|
||||
"label": "The desired SQL transaction isolation level.",
|
||||
"helpText": "The desired SQL transaction isolation level.",
|
||||
"is_optional": true,
|
||||
"regexes": [
|
||||
"^[0-9A-Z_]+$"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "cutoffTime",
|
||||
"label": "The maximum history modification time of a contact history entry eligible for wipe out.",
|
||||
"helpText": "If the history modificaiton time of contact history entry is older than this, and it is not the most recent entry of a contact, it will have its PII wiped out.",
|
||||
"is_optional": true
|
||||
},
|
||||
{
|
||||
"name": "isDryRun",
|
||||
"label": "Whether this job is a dry run.",
|
||||
"helpText": "If true, no changes will be saved to the database.",
|
||||
"is_optional": true,
|
||||
"regexes": [
|
||||
"^true|false$"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20,16 +20,11 @@
|
||||
{@param domain: string}
|
||||
{@param add: bool}
|
||||
{@param addNameservers: list<string>}
|
||||
{@param addAdmins: list<string>}
|
||||
{@param addTechs: list<string>}
|
||||
{@param addStatuses: list<string>}
|
||||
{@param remove: bool}
|
||||
{@param removeNameservers: list<string>}
|
||||
{@param removeAdmins: list<string>}
|
||||
{@param removeTechs: list<string>}
|
||||
{@param removeStatuses: list<string>}
|
||||
{@param change: bool}
|
||||
{@param? registrant: string|null}
|
||||
{@param? password: string|null}
|
||||
{@param secdns: bool}
|
||||
{@param addDsRecords: list<[keyTag:int, alg:int, digestType:int, digest:string]>}
|
||||
@@ -54,12 +49,6 @@
|
||||
{/for}
|
||||
</domain:ns>
|
||||
{/if}
|
||||
{for $admin in $addAdmins}
|
||||
<domain:contact type="admin">{$admin}</domain:contact>
|
||||
{/for}
|
||||
{for $tech in $addTechs}
|
||||
<domain:contact type="tech">{$tech}</domain:contact>
|
||||
{/for}
|
||||
{for $status in $addStatuses}
|
||||
<domain:status s="{$status}"/>
|
||||
{/for}
|
||||
@@ -74,12 +63,6 @@
|
||||
{/for}
|
||||
</domain:ns>
|
||||
{/if}
|
||||
{for $admin in $removeAdmins}
|
||||
<domain:contact type="admin">{$admin}</domain:contact>
|
||||
{/for}
|
||||
{for $tech in $removeTechs}
|
||||
<domain:contact type="tech">{$tech}</domain:contact>
|
||||
{/for}
|
||||
{for $status in $removeStatuses}
|
||||
<domain:status s="{$status}"/>
|
||||
{/for}
|
||||
@@ -87,9 +70,6 @@
|
||||
{/if}
|
||||
{if $change}
|
||||
<domain:chg>
|
||||
{if $registrant}
|
||||
<domain:registrant>{$registrant}</domain:registrant>
|
||||
{/if}
|
||||
{if $password}
|
||||
<domain:authInfo>
|
||||
<domain:pw>{$password}</domain:pw>
|
||||
|
||||
@@ -18,12 +18,12 @@ import static google.registry.batch.AsyncTaskEnqueuer.PARAM_REQUESTED_TIME;
|
||||
import static google.registry.batch.AsyncTaskEnqueuer.PARAM_RESAVE_TIMES;
|
||||
import static google.registry.batch.AsyncTaskEnqueuer.PARAM_RESOURCE_KEY;
|
||||
import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.TestLogHandlerUtils.assertLogMessage;
|
||||
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
@@ -67,9 +67,9 @@ public class AsyncTaskEnqueuerTest {
|
||||
|
||||
@Test
|
||||
void test_enqueueAsyncResave_success() {
|
||||
Contact contact = persistActiveContact("jd23456");
|
||||
Host host = persistActiveHost("ns1.example.tld");
|
||||
asyncTaskEnqueuer.enqueueAsyncResave(
|
||||
contact.createVKey(), clock.nowUtc(), ImmutableSortedSet.of(clock.nowUtc().plusDays(5)));
|
||||
host.createVKey(), clock.nowUtc(), ImmutableSortedSet.of(clock.nowUtc().plusDays(5)));
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new CloudTasksHelper.TaskMatcher()
|
||||
@@ -77,17 +77,17 @@ public class AsyncTaskEnqueuerTest {
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.header("content-type", "application/x-www-form-urlencoded")
|
||||
.param(PARAM_RESOURCE_KEY, contact.createVKey().stringify())
|
||||
.param(PARAM_RESOURCE_KEY, host.createVKey().stringify())
|
||||
.param(PARAM_REQUESTED_TIME, clock.nowUtc().toString())
|
||||
.scheduleTime(clock.nowUtc().plus(Duration.standardDays(5))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_enqueueAsyncResave_multipleResaves() {
|
||||
Contact contact = persistActiveContact("jd23456");
|
||||
Host host = persistActiveHost("ns1.example.tld");
|
||||
DateTime now = clock.nowUtc();
|
||||
asyncTaskEnqueuer.enqueueAsyncResave(
|
||||
contact.createVKey(),
|
||||
host.createVKey(),
|
||||
now,
|
||||
ImmutableSortedSet.of(now.plusHours(24), now.plusHours(50), now.plusHours(75)));
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
@@ -97,7 +97,7 @@ public class AsyncTaskEnqueuerTest {
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.header("content-type", "application/x-www-form-urlencoded")
|
||||
.param(PARAM_RESOURCE_KEY, contact.createVKey().stringify())
|
||||
.param(PARAM_RESOURCE_KEY, host.createVKey().stringify())
|
||||
.param(PARAM_REQUESTED_TIME, now.toString())
|
||||
.param(PARAM_RESAVE_TIMES, "2015-05-20T14:34:56.000Z,2015-05-21T15:34:56.000Z")
|
||||
.scheduleTime(clock.nowUtc().plus(Duration.standardHours(24))));
|
||||
@@ -106,9 +106,9 @@ public class AsyncTaskEnqueuerTest {
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@Test
|
||||
void test_enqueueAsyncResave_ignoresTasksTooFarIntoFuture() {
|
||||
Contact contact = persistActiveContact("jd23456");
|
||||
Host host = persistActiveHost("ns1.example.tld");
|
||||
asyncTaskEnqueuer.enqueueAsyncResave(
|
||||
contact.createVKey(), clock.nowUtc(), ImmutableSortedSet.of(clock.nowUtc().plusDays(31)));
|
||||
host.createVKey(), clock.nowUtc(), ImmutableSortedSet.of(clock.nowUtc().plusDays(31)));
|
||||
cloudTasksHelper.assertNoTasksEnqueued(QUEUE_ASYNC_ACTIONS);
|
||||
assertLogMessage(logHandler, Level.INFO, "Ignoring async re-save");
|
||||
}
|
||||
|
||||
@@ -65,23 +65,18 @@ public class BulkDomainTransferActionTest {
|
||||
// The default registrar is TheRegistrar, which will be the losing registrar
|
||||
activeDomain =
|
||||
persistDomainWithDependentResources(
|
||||
"active", "tld", null, now, now.minusDays(1), DateTimeUtils.END_OF_TIME);
|
||||
"active", "tld", now, now.minusDays(1), DateTimeUtils.END_OF_TIME);
|
||||
alreadyTransferredDomain =
|
||||
persistResource(
|
||||
persistDomainWithDependentResources(
|
||||
"alreadytransferred",
|
||||
"tld",
|
||||
null,
|
||||
now,
|
||||
now.minusDays(1),
|
||||
DateTimeUtils.END_OF_TIME)
|
||||
"alreadytransferred", "tld", now, now.minusDays(1), DateTimeUtils.END_OF_TIME)
|
||||
.asBuilder()
|
||||
.setPersistedCurrentSponsorRegistrarId("NewRegistrar")
|
||||
.build());
|
||||
pendingDeleteDomain =
|
||||
persistResource(
|
||||
persistDomainWithDependentResources(
|
||||
"pendingdelete", "tld", null, now, now.minusDays(1), now.plusMonths(1))
|
||||
"pendingdelete", "tld", now, now.minusDays(1), now.plusMonths(1))
|
||||
.asBuilder()
|
||||
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
|
||||
.build());
|
||||
|
||||
@@ -16,7 +16,6 @@ package google.registry.batch;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistEppResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.LogsSubject.assertAboutLogs;
|
||||
@@ -32,7 +31,6 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.testing.TestLogHandler;
|
||||
import google.registry.groups.GmailClient;
|
||||
import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
@@ -79,7 +77,6 @@ public class CheckBulkComplianceActionTest {
|
||||
private final Logger loggerToIntercept =
|
||||
Logger.getLogger(CheckBulkComplianceAction.class.getCanonicalName());
|
||||
private final GmailClient gmailClient = mock(GmailClient.class);
|
||||
private Contact contact;
|
||||
private BulkPricingPackage bulkPricingPackage;
|
||||
private SendEmailUtils sendEmailUtils;
|
||||
private ArgumentCaptor<EmailMessage> emailCaptor = ArgumentCaptor.forClass(EmailMessage.class);
|
||||
@@ -125,8 +122,6 @@ public class CheckBulkComplianceActionTest {
|
||||
.setNextBillingDate(DateTime.parse("2012-11-12T05:00:00Z"))
|
||||
.setLastNotificationSent(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.build();
|
||||
|
||||
contact = persistActiveContact("contact1234");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@@ -138,7 +133,7 @@ public class CheckBulkComplianceActionTest {
|
||||
void testSuccess_noBulkPackageOverCreateLimit() {
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
DatabaseHelper.newDomain("foo.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
@@ -156,12 +151,12 @@ public class CheckBulkComplianceActionTest {
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
// Create limit is 1, creating 2 domains to go over the limit
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
DatabaseHelper.newDomain("foo.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz.tld", contact)
|
||||
DatabaseHelper.newDomain("buzz.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
@@ -189,12 +184,12 @@ public class CheckBulkComplianceActionTest {
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
// Create limit is 1, creating 2 domains to go over the limit
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
DatabaseHelper.newDomain("foo.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz.tld", contact)
|
||||
DatabaseHelper.newDomain("buzz.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
@@ -223,12 +218,12 @@ public class CheckBulkComplianceActionTest {
|
||||
tm().transact(() -> tm().put(bulkPricingPackage2));
|
||||
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo2.tld", contact)
|
||||
DatabaseHelper.newDomain("foo2.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token2.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz2.tld", contact)
|
||||
DatabaseHelper.newDomain("buzz2.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token2.createVKey())
|
||||
.build());
|
||||
@@ -282,12 +277,12 @@ public class CheckBulkComplianceActionTest {
|
||||
|
||||
// Create limit is 1, creating 2 domains to go over the limit
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
DatabaseHelper.newDomain("foo.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token2.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz.tld", contact)
|
||||
DatabaseHelper.newDomain("buzz.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token2.createVKey())
|
||||
.build());
|
||||
@@ -304,7 +299,7 @@ public class CheckBulkComplianceActionTest {
|
||||
void testSuccess_noBulkPricingPackageOverActiveDomainsLimit() {
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
DatabaseHelper.newDomain("foo.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
@@ -323,12 +318,12 @@ public class CheckBulkComplianceActionTest {
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
// Domains limit is 1, creating 2 domains to go over the limit
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
DatabaseHelper.newDomain("foo.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz.tld", contact)
|
||||
DatabaseHelper.newDomain("buzz.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
@@ -356,7 +351,7 @@ public class CheckBulkComplianceActionTest {
|
||||
.build();
|
||||
tm().transact(() -> tm().put(bulkPricingPackage2));
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo2.tld", contact)
|
||||
DatabaseHelper.newDomain("foo2.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token2.createVKey())
|
||||
.build());
|
||||
@@ -390,12 +385,12 @@ public class CheckBulkComplianceActionTest {
|
||||
tm().put(bulkPricingPackage.asBuilder().setMaxDomains(1).setMaxCreates(4).build()));
|
||||
// Domains limit is 1, creating 2 domains to go over the limit
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
DatabaseHelper.newDomain("foo.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz.tld", contact)
|
||||
DatabaseHelper.newDomain("buzz.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
@@ -424,12 +419,12 @@ public class CheckBulkComplianceActionTest {
|
||||
tm().transact(() -> tm().put(bulkPricingPackage2));
|
||||
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo2.tld", contact)
|
||||
DatabaseHelper.newDomain("foo2.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token2.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz2.tld", contact)
|
||||
DatabaseHelper.newDomain("buzz2.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token2.createVKey())
|
||||
.build());
|
||||
@@ -467,12 +462,12 @@ public class CheckBulkComplianceActionTest {
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
// Domains limit is 1, creating 2 domains to go over the limit
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
DatabaseHelper.newDomain("foo.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz.tld", contact)
|
||||
DatabaseHelper.newDomain("buzz.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
@@ -508,12 +503,12 @@ public class CheckBulkComplianceActionTest {
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
// Domains limit is 1, creating 2 domains to go over the limit
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
DatabaseHelper.newDomain("foo.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz.tld", contact)
|
||||
DatabaseHelper.newDomain("buzz.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
@@ -553,12 +548,12 @@ public class CheckBulkComplianceActionTest {
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
// Domains limit is 1, creating 2 domains to go over the limit
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
DatabaseHelper.newDomain("foo.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz.tld", contact)
|
||||
DatabaseHelper.newDomain("buzz.tld")
|
||||
.asBuilder()
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
// 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.batch;
|
||||
|
||||
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.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.newDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.util.concurrent.RateLimiter;
|
||||
import google.registry.flows.DaggerEppTestComponent;
|
||||
import google.registry.flows.EppController;
|
||||
import google.registry.flows.EppTestComponent.FakesAndMocksModule;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeLockHandler;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link RemoveAllDomainContactsAction}. */
|
||||
class RemoveAllDomainContactsActionTest {
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
private final FakeResponse response = new FakeResponse();
|
||||
private final RateLimiter rateLimiter = mock(RateLimiter.class);
|
||||
private RemoveAllDomainContactsAction action;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, ACTIVE))
|
||||
.build());
|
||||
EppController eppController =
|
||||
DaggerEppTestComponent.builder()
|
||||
.fakesAndMocksModule(FakesAndMocksModule.create(new FakeClock()))
|
||||
.build()
|
||||
.startRequest()
|
||||
.eppController();
|
||||
action =
|
||||
new RemoveAllDomainContactsAction(
|
||||
eppController, "NewRegistrar", new FakeLockHandler(true), rateLimiter, response);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_removesAllContactsFromMultipleDomains_andDoesntModifyDomainThatHasNoContacts() {
|
||||
Contact c1 = persistActiveContact("contact12345");
|
||||
Domain d1 = persistResource(newDomain("foo.tld", c1));
|
||||
assertThat(d1.getAllContacts()).hasSize(3);
|
||||
Contact c2 = persistActiveContact("contact23456");
|
||||
Domain d2 = persistResource(newDomain("bar.tld", c2));
|
||||
assertThat(d2.getAllContacts()).hasSize(3);
|
||||
Domain d3 =
|
||||
persistResource(
|
||||
newDomain("baz.tld")
|
||||
.asBuilder()
|
||||
.setRegistrant(Optional.empty())
|
||||
.setContacts(ImmutableSet.of())
|
||||
.build());
|
||||
assertThat(d3.getAllContacts()).isEmpty();
|
||||
DateTime lastUpdate = d3.getUpdateTimestamp().getTimestamp();
|
||||
|
||||
action.run();
|
||||
assertThat(loadByEntity(d1).getAllContacts()).isEmpty();
|
||||
assertThat(loadByEntity(d2).getAllContacts()).isEmpty();
|
||||
assertThat(loadByEntity(d3).getUpdateTimestamp().getTimestamp()).isEqualTo(lastUpdate);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_removesContacts_onDomainsThatOnlyPartiallyHaveContacts() {
|
||||
Contact c1 = persistActiveContact("contact12345");
|
||||
Domain d1 =
|
||||
persistResource(
|
||||
newDomain("foo.tld", c1).asBuilder().setContacts(ImmutableSet.of()).build());
|
||||
assertThat(d1.getAllContacts()).hasSize(1);
|
||||
Contact c2 = persistActiveContact("contact23456");
|
||||
Domain d2 =
|
||||
persistResource(
|
||||
newDomain("bar.tld", c2).asBuilder().setRegistrant(Optional.empty()).build());
|
||||
assertThat(d2.getAllContacts()).hasSize(2);
|
||||
|
||||
action.run();
|
||||
assertThat(loadByEntity(d1).getAllContacts()).isEmpty();
|
||||
assertThat(loadByEntity(d2).getAllContacts()).isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.newDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistDomainWithDependentResources;
|
||||
import static google.registry.testing.DatabaseHelper.persistDomainWithPendingTransfer;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
@@ -87,7 +86,6 @@ public class ResaveEntityActionTest {
|
||||
persistDomainWithDependentResources(
|
||||
"domain",
|
||||
"tld",
|
||||
persistActiveContact("jd1234"),
|
||||
DateTime.parse("2016-02-06T10:00:00Z"),
|
||||
DateTime.parse("2016-02-06T10:00:00Z"),
|
||||
DateTime.parse("2017-01-02T10:11:00Z")),
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
// Copyright 2021 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.batch;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.api.services.dataflow.model.LaunchFlexTemplateRequest;
|
||||
import google.registry.beam.BeamActionTestBase;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
/** Unit tests for {@link WipeOutContactHistoryPiiAction}. */
|
||||
class WipeOutContactHistoryPiiActionTest extends BeamActionTestBase {
|
||||
|
||||
private final DateTime now = DateTime.parse("2019-01-19T01:02:03Z");
|
||||
private final FakeClock clock = new FakeClock(now);
|
||||
private final Map<String, String> expectedParameters = new HashMap<>();
|
||||
private final ArgumentCaptor<LaunchFlexTemplateRequest> launchRequest =
|
||||
ArgumentCaptor.forClass(LaunchFlexTemplateRequest.class);
|
||||
private WipeOutContactHistoryPiiAction action =
|
||||
new WipeOutContactHistoryPiiAction(
|
||||
clock,
|
||||
false,
|
||||
Optional.empty(),
|
||||
8,
|
||||
"tucketBucket",
|
||||
"testProject",
|
||||
"testRegion",
|
||||
dataflow,
|
||||
response);
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
expectedParameters.put("registryEnvironment", "UNITTEST");
|
||||
expectedParameters.put("isDryRun", "false");
|
||||
expectedParameters.put("cutoffTime", "2018-05-19T01:02:03.000Z");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess() throws Exception {
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo("Launched contact history PII wipeout pipeline: jobid");
|
||||
verify(templates, times(1))
|
||||
.launch(eq("testProject"), eq("testRegion"), launchRequest.capture());
|
||||
assertThat(launchRequest.getValue().getLaunchParameter().getParameters())
|
||||
.containsExactlyEntriesIn(expectedParameters);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_providedCutoffTime() throws Exception {
|
||||
action =
|
||||
new WipeOutContactHistoryPiiAction(
|
||||
clock,
|
||||
false,
|
||||
Optional.of(now.minusYears(1)),
|
||||
8,
|
||||
"tucketBucket",
|
||||
"testProject",
|
||||
"testRegion",
|
||||
dataflow,
|
||||
response);
|
||||
action.run();
|
||||
expectedParameters.put("cutoffTime", "2018-01-19T01:02:03.000Z");
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo("Launched contact history PII wipeout pipeline: jobid");
|
||||
verify(templates, times(1))
|
||||
.launch(eq("testProject"), eq("testRegion"), launchRequest.capture());
|
||||
assertThat(launchRequest.getValue().getLaunchParameter().getParameters())
|
||||
.containsExactlyEntriesIn(expectedParameters);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_dryRun() throws Exception {
|
||||
action =
|
||||
new WipeOutContactHistoryPiiAction(
|
||||
clock,
|
||||
true,
|
||||
Optional.empty(),
|
||||
8,
|
||||
"tucketBucket",
|
||||
"testProject",
|
||||
"testRegion",
|
||||
dataflow,
|
||||
response);
|
||||
action.run();
|
||||
expectedParameters.put("isDryRun", "true");
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo("Launched contact history PII wipeout pipeline: jobid");
|
||||
verify(templates, times(1))
|
||||
.launch(eq("testProject"), eq("testRegion"), launchRequest.capture());
|
||||
assertThat(launchRequest.getValue().getLaunchParameter().getParameters())
|
||||
.containsExactlyEntriesIn(expectedParameters);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_launchError() throws Exception {
|
||||
when(launch.execute()).thenThrow(new IOException("cannot launch"));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(500);
|
||||
assertThat(response.getPayload()).isEqualTo("Pipeline launch failed: cannot launch");
|
||||
verify(templates, times(1))
|
||||
.launch(eq("testProject"), eq("testRegion"), launchRequest.capture());
|
||||
assertThat(launchRequest.getValue().getLaunchParameter().getParameters())
|
||||
.containsExactlyEntriesIn(expectedParameters);
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
package google.registry.beam.common;
|
||||
|
||||
import static google.registry.persistence.transaction.JpaTransactionManagerExtension.makeRegistrar1;
|
||||
import static google.registry.testing.DatabaseHelper.newContact;
|
||||
import static google.registry.testing.DatabaseHelper.newHost;
|
||||
import static google.registry.testing.DatabaseHelper.newTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistResources;
|
||||
@@ -27,8 +27,6 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.beam.TestPipelineExtension;
|
||||
import google.registry.beam.common.RegistryJpaIO.Read;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactBase;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainAuthInfo;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
@@ -37,14 +35,14 @@ import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.secdns.DomainDsData;
|
||||
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.host.HostBase;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.transfer.ContactTransferData;
|
||||
import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.util.Optional;
|
||||
import org.apache.beam.sdk.coders.StringUtf8Coder;
|
||||
import org.apache.beam.sdk.testing.PAssert;
|
||||
import org.apache.beam.sdk.values.PCollection;
|
||||
@@ -71,32 +69,33 @@ public class RegistryJpaReadTest {
|
||||
final transient TestPipelineExtension testPipeline =
|
||||
TestPipelineExtension.create().enableAbandonedNodeEnforcement(true);
|
||||
|
||||
private transient ImmutableList<Contact> contacts;
|
||||
private transient ImmutableList<Host> hosts;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
Registrar ofyRegistrar = JpaIntegrationTestExtension.makeRegistrar2();
|
||||
persistResource(ofyRegistrar);
|
||||
|
||||
ImmutableList.Builder<Contact> builder = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<Host> builder = new ImmutableList.Builder<>();
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Contact contact = newContact("contact_" + i);
|
||||
builder.add(contact);
|
||||
Host host = newHost(String.format("ns%d.example.tld", i));
|
||||
builder.add(host);
|
||||
}
|
||||
contacts = builder.build();
|
||||
persistResources(contacts);
|
||||
hosts = builder.build();
|
||||
persistResources(hosts);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readWithCriteriaQuery() {
|
||||
Read<Contact, String> read =
|
||||
Read<Host, String> read =
|
||||
RegistryJpaIO.read(
|
||||
() -> CriteriaQueryBuilder.create(Contact.class).build(), ContactBase::getContactId)
|
||||
() -> CriteriaQueryBuilder.create(Host.class).build(), HostBase::getHostName)
|
||||
.withCoder(StringUtf8Coder.of());
|
||||
PCollection<String> repoIds = testPipeline.apply(read);
|
||||
|
||||
PAssert.that(repoIds).containsInAnyOrder("contact_0", "contact_1", "contact_2");
|
||||
PAssert.that(repoIds)
|
||||
.containsInAnyOrder("ns0.example.tld", "ns1.example.tld", "ns2.example.tld");
|
||||
testPipeline.run();
|
||||
}
|
||||
|
||||
@@ -170,13 +169,6 @@ public class RegistryJpaReadTest {
|
||||
.setRegistrarId("registrar1")
|
||||
.setEmailAddress("me@google.com")
|
||||
.build();
|
||||
Contact contact =
|
||||
new Contact.Builder()
|
||||
.setRepoId("contactid_1")
|
||||
.setCreationRegistrarId(registrar.getRegistrarId())
|
||||
.setTransferData(new ContactTransferData.Builder().build())
|
||||
.setPersistedCurrentSponsorRegistrarId(registrar.getRegistrarId())
|
||||
.build();
|
||||
Domain domain =
|
||||
new Domain.Builder()
|
||||
.setDomainName("example.com")
|
||||
@@ -193,7 +185,6 @@ public class RegistryJpaReadTest {
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED,
|
||||
StatusValue.SERVER_RENEW_PROHIBITED,
|
||||
StatusValue.SERVER_HOLD))
|
||||
.setRegistrant(Optional.of(contact.createVKey()))
|
||||
.setContacts(ImmutableSet.of())
|
||||
.setSubordinateHosts(ImmutableSet.of("ns1.example.com"))
|
||||
.setPersistedCurrentSponsorRegistrarId(registrar.getRegistrarId())
|
||||
@@ -212,6 +203,6 @@ public class RegistryJpaReadTest {
|
||||
null,
|
||||
100L))
|
||||
.build();
|
||||
persistResources(registry, registrar, contact, domain);
|
||||
persistResources(registry, registrar, domain);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,22 +18,18 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.immutableObjectCorrespondence;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.loadAllOf;
|
||||
import static google.registry.testing.DatabaseHelper.newContact;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static google.registry.testing.DatabaseHelper.newHost;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import google.registry.beam.TestPipelineExtension;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.persistence.transaction.JpaTransactionManagerExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.io.Serializable;
|
||||
import org.apache.beam.sdk.Pipeline.PipelineExecutionException;
|
||||
import org.apache.beam.sdk.transforms.Create;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
@@ -53,42 +49,18 @@ class RegistryJpaWriteTest implements Serializable {
|
||||
@Test
|
||||
void writeToSql_twoWriters() {
|
||||
tm().transact(() -> tm().put(JpaTransactionManagerExtension.makeRegistrar2()));
|
||||
ImmutableList.Builder<Contact> contactsBuilder = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<Host> hostsBuilder = new ImmutableList.Builder<>();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
contactsBuilder.add(newContact("contact_" + i));
|
||||
hostsBuilder.add(newHost(String.format("ns%d.example.tld", i)));
|
||||
}
|
||||
ImmutableList<Contact> contacts = contactsBuilder.build();
|
||||
ImmutableList<Host> hosts = hostsBuilder.build();
|
||||
testPipeline
|
||||
.apply(Create.of(contacts))
|
||||
.apply(RegistryJpaIO.<Contact>write().withName("Contact").withBatchSize(4));
|
||||
.apply(Create.of(hosts))
|
||||
.apply(RegistryJpaIO.<Host>write().withName("Host").withBatchSize(4));
|
||||
testPipeline.run().waitUntilFinish();
|
||||
|
||||
assertThat(loadAllOf(Contact.class))
|
||||
assertThat(loadAllOf(Host.class))
|
||||
.comparingElementsUsing(immutableObjectCorrespondence("revisions", "updateTimestamp"))
|
||||
.containsExactlyElementsIn(contacts);
|
||||
}
|
||||
|
||||
@Disabled("b/263502442")
|
||||
@Test
|
||||
void testFailure_writeExistingEntity() {
|
||||
// RegistryJpaIO.Write actions should not write existing objects to the database because the
|
||||
// object could have been mutated in between creation and when the Write actually occurs,
|
||||
// causing a race condition
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().put(JpaTransactionManagerExtension.makeRegistrar2());
|
||||
tm().put(newContact("contact"));
|
||||
});
|
||||
Contact contact = Iterables.getOnlyElement(loadAllOf(Contact.class));
|
||||
testPipeline
|
||||
.apply(Create.of(contact))
|
||||
.apply(RegistryJpaIO.<Contact>write().withName("Contact"));
|
||||
// PipelineExecutionException caused by a RuntimeException caused by an IllegalArgumentException
|
||||
assertThat(
|
||||
assertThrows(
|
||||
PipelineExecutionException.class, () -> testPipeline.run().waitUntilFinish()))
|
||||
.hasCauseThat()
|
||||
.hasCauseThat()
|
||||
.isInstanceOf(IllegalArgumentException.class);
|
||||
.containsExactlyElementsIn(hosts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,13 +25,11 @@ import static google.registry.model.rde.RdeMode.THIN;
|
||||
import static google.registry.persistence.transaction.JpaTransactionManagerExtension.makeRegistrar1;
|
||||
import static google.registry.persistence.transaction.JpaTransactionManagerExtension.makeRegistrar2;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.rde.RdeResourceType.CONTACT;
|
||||
import static google.registry.rde.RdeResourceType.DOMAIN;
|
||||
import static google.registry.rde.RdeResourceType.HOST;
|
||||
import static google.registry.rde.RdeResourceType.REGISTRAR;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.newDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistEppResource;
|
||||
@@ -54,10 +52,6 @@ import google.registry.gcs.GcsUtils;
|
||||
import google.registry.keyring.api.PgpHelper;
|
||||
import google.registry.model.common.Cursor;
|
||||
import google.registry.model.common.Cursor.CursorType;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactBase;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
@@ -114,8 +108,6 @@ public class RdePipelineTest {
|
||||
|
||||
private static final String DOMAIN_NAME_PATTERN = "<rdeDomain:name>(.*)</rdeDomain:name>";
|
||||
|
||||
private static final String CONTACT_ID_PATTERN = "<rdeContact:id>(.*)</rdeContact:id>";
|
||||
|
||||
private static final String HOST_NAME_PATTERN = "<rdeHost:name>(.*)</rdeHost:name>";
|
||||
|
||||
// This is the default creation time for test data.
|
||||
@@ -139,7 +131,6 @@ public class RdePipelineTest {
|
||||
ImmutableList.of(
|
||||
DepositFragment.create(DOMAIN, "<rdeDomain:domain/>\n", ""),
|
||||
DepositFragment.create(REGISTRAR, "<rdeRegistrar:registrar/>\n", ""),
|
||||
DepositFragment.create(CONTACT, "<rdeContact:contact/>\n", ""),
|
||||
DepositFragment.create(HOST, "<rdeHost:host/>\n", ""));
|
||||
|
||||
private final GcsUtils gcsUtils = new GcsUtils(LocalStorageHelper.getOptions());
|
||||
@@ -165,21 +156,6 @@ public class RdePipelineTest {
|
||||
|
||||
private RdePipeline rdePipeline;
|
||||
|
||||
private ContactHistory persistContactHistory(ContactBase contact) {
|
||||
return persistResource(
|
||||
new ContactHistory.Builder()
|
||||
.setType(HistoryEntry.Type.HOST_CREATE)
|
||||
.setXmlBytes("<xml></xml>".getBytes(UTF_8))
|
||||
.setModificationTime(clock.nowUtc())
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setTrid(Trid.create("ABC-123", "server-trid"))
|
||||
.setBySuperuser(false)
|
||||
.setReason("reason")
|
||||
.setRequestedByRegistrar(true)
|
||||
.setContact(contact)
|
||||
.build());
|
||||
}
|
||||
|
||||
private DomainHistory persistDomainHistory(DomainBase domain) {
|
||||
DomainTransactionRecord transactionRecord =
|
||||
new DomainTransactionRecord.Builder()
|
||||
@@ -254,20 +230,18 @@ public class RdePipelineTest {
|
||||
RdeRevision.saveRevision("soy", now, FULL, 0);
|
||||
});
|
||||
|
||||
// This contact is never referenced.
|
||||
persistContactHistory(persistActiveContact("contactX"));
|
||||
Contact contact1 = persistActiveContact("contact1234");
|
||||
persistContactHistory(contact1);
|
||||
Contact contact2 = persistActiveContact("contact456");
|
||||
persistContactHistory(contact2);
|
||||
|
||||
// This host is never referenced.
|
||||
persistHostHistory(persistActiveHost("ns0.domain.tld"));
|
||||
Host host1 = persistActiveHost("ns1.external.tld");
|
||||
persistHostHistory(host1);
|
||||
Domain helloDomain =
|
||||
persistEppResource(
|
||||
newDomain("hello.soy", contact1).asBuilder().addNameserver(host1.createVKey()).build());
|
||||
newDomain("hello.soy")
|
||||
.asBuilder()
|
||||
.addNameserver(host1.createVKey())
|
||||
.setRegistrant(Optional.empty())
|
||||
.setContacts(ImmutableSet.of())
|
||||
.build());
|
||||
persistDomainHistory(helloDomain);
|
||||
persistHostHistory(persistActiveHost("not-used-subordinate.hello.soy"));
|
||||
Host host2 = persistActiveHost("ns1.hello.soy");
|
||||
@@ -276,14 +250,15 @@ public class RdePipelineTest {
|
||||
// This domain has no registrant.
|
||||
Domain kittyDomain =
|
||||
persistEppResource(
|
||||
newDomain("kitty.fun", contact2)
|
||||
newDomain("kitty.fun")
|
||||
.asBuilder()
|
||||
.addNameservers(ImmutableSet.of(host1.createVKey(), host2.createVKey()))
|
||||
.setRegistrant(Optional.empty())
|
||||
.setContacts(ImmutableSet.of())
|
||||
.build());
|
||||
persistDomainHistory(kittyDomain);
|
||||
// Should not appear because the TLD is not included in a pending deposit.
|
||||
persistDomainHistory(persistEppResource(newDomain("lol.cat", contact1)));
|
||||
persistDomainHistory(persistEppResource(newDomain("lol.cat")));
|
||||
// To be deleted.
|
||||
Domain deletedDomain = persistActiveDomain("deleted.soy");
|
||||
persistDomainHistory(deletedDomain);
|
||||
@@ -293,8 +268,7 @@ public class RdePipelineTest {
|
||||
persistDomainHistory(deletedDomain.asBuilder().setDeletionTime(clock.nowUtc()).build());
|
||||
kittyDomain = kittyDomain.asBuilder().setDomainName("cat.fun").build();
|
||||
persistDomainHistory(kittyDomain);
|
||||
Contact contact3 = persistActiveContact("contact789");
|
||||
persistContactHistory(contact3);
|
||||
|
||||
// This is a subordinate domain in TLD .cat, which is not included in any pending deposit. But
|
||||
// it should still be included as a subordinate host in the pendign deposit for .soy.
|
||||
Host host3 = persistActiveHost("ns1.lol.cat");
|
||||
@@ -302,17 +276,8 @@ public class RdePipelineTest {
|
||||
persistDomainHistory(
|
||||
helloDomain
|
||||
.asBuilder()
|
||||
.removeContacts(
|
||||
helloDomain.getContacts().stream()
|
||||
.filter(dc -> dc.getType() == DesignatedContact.Type.ADMIN)
|
||||
.collect(toImmutableSet()))
|
||||
.addContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(DesignatedContact.Type.ADMIN, contact3.createVKey())))
|
||||
.addNameserver(host3.createVKey())
|
||||
.build());
|
||||
// contact456 is renamed to contactABC.
|
||||
persistContactHistory(contact2.asBuilder().setContactId("contactABC").build());
|
||||
// ns1.hello.soy is renamed to ns2.hello.soy
|
||||
persistHostHistory(host2.asBuilder().setHostName("ns2.hello.soy").build());
|
||||
|
||||
@@ -320,18 +285,11 @@ public class RdePipelineTest {
|
||||
// resulting deposit fragments.
|
||||
clock.advanceBy(Duration.standardDays(2));
|
||||
persistDomainHistory(kittyDomain.asBuilder().setDeletionTime(clock.nowUtc()).build());
|
||||
Contact futureContact = persistActiveContact("future-contact");
|
||||
persistContactHistory(futureContact);
|
||||
Host futureHost = persistActiveHost("ns1.future.tld");
|
||||
persistHostHistory(futureHost);
|
||||
persistDomainHistory(
|
||||
persistEppResource(
|
||||
newDomain("future.soy", futureContact)
|
||||
.asBuilder()
|
||||
.setNameservers(futureHost.createVKey())
|
||||
.build()));
|
||||
// contactABC is renamed to contactXYZ.
|
||||
persistContactHistory(contact2.asBuilder().setContactId("contactXYZ").build());
|
||||
newDomain("future.soy").asBuilder().setNameservers(futureHost.createVKey()).build()));
|
||||
// ns2.hello.soy is renamed to ns3.hello.soy
|
||||
persistHostHistory(host2.asBuilder().setHostName("ns3.hello.soy").build());
|
||||
|
||||
@@ -390,11 +348,9 @@ public class RdePipelineTest {
|
||||
"""
|
||||
<rdeDomain:domain>
|
||||
<rdeDomain:name>cat.fun</rdeDomain:name>
|
||||
<rdeDomain:roid>15-FUN</rdeDomain:roid>
|
||||
<rdeDomain:roid>F-FUN</rdeDomain:roid>
|
||||
<rdeDomain:uName>cat.fun</rdeDomain:uName>
|
||||
<rdeDomain:status s="ok"/>
|
||||
<rdeDomain:contact type="admin">contact456</rdeDomain:contact>
|
||||
<rdeDomain:contact type="tech">contact456</rdeDomain:contact>
|
||||
<rdeDomain:ns>
|
||||
<domain:hostObj>ns1.external.tld</domain:hostObj>
|
||||
<domain:hostObj>ns1.hello.soy</domain:hostObj>
|
||||
@@ -407,14 +363,7 @@ public class RdePipelineTest {
|
||||
""");
|
||||
}
|
||||
if (kv.getKey().mode().equals(FULL)) {
|
||||
// Contact fragments for hello.soy.
|
||||
if ("soy".equals(kv.getKey().tld())) {
|
||||
assertThat(
|
||||
getFragmentForType(kv, CONTACT)
|
||||
.map(getXmlElement(CONTACT_ID_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("contact1234", "contact789");
|
||||
|
||||
// Host fragments for hello.soy.
|
||||
assertThat(
|
||||
getFragmentForType(kv, HOST)
|
||||
@@ -428,12 +377,9 @@ public class RdePipelineTest {
|
||||
"""
|
||||
<rdeDomain:domain>
|
||||
<rdeDomain:name>hello.soy</rdeDomain:name>
|
||||
<rdeDomain:roid>E-SOY</rdeDomain:roid>
|
||||
<rdeDomain:roid>8-SOY</rdeDomain:roid>
|
||||
<rdeDomain:uName>hello.soy</rdeDomain:uName>
|
||||
<rdeDomain:status s="ok"/>
|
||||
<rdeDomain:registrant>contact1234</rdeDomain:registrant>
|
||||
<rdeDomain:contact type="admin">contact789</rdeDomain:contact>
|
||||
<rdeDomain:contact type="tech">contact1234</rdeDomain:contact>
|
||||
<rdeDomain:ns>
|
||||
<domain:hostObj>ns1.external.tld</domain:hostObj>
|
||||
<domain:hostObj>ns1.lol.cat</domain:hostObj>
|
||||
@@ -445,13 +391,6 @@ public class RdePipelineTest {
|
||||
</rdeDomain:domain>\
|
||||
""");
|
||||
} else {
|
||||
// Contact fragments for cat.fun.
|
||||
assertThat(
|
||||
getFragmentForType(kv, CONTACT)
|
||||
.map(getXmlElement(CONTACT_ID_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("contactABC");
|
||||
|
||||
// Host fragments for cat.soy.
|
||||
assertThat(
|
||||
getFragmentForType(kv, HOST)
|
||||
@@ -460,22 +399,19 @@ public class RdePipelineTest {
|
||||
.containsExactly("ns1.external.tld", "ns2.hello.soy");
|
||||
}
|
||||
} else {
|
||||
// BRDA does not contain contact or hosts.
|
||||
// BRDA does not contain hosts.
|
||||
assertThat(
|
||||
Streams.stream(kv.getValue())
|
||||
.anyMatch(
|
||||
fragment ->
|
||||
fragment.type().equals(CONTACT)
|
||||
|| fragment.type().equals(HOST)))
|
||||
.anyMatch(fragment -> fragment.type().equals(HOST)))
|
||||
.isFalse();
|
||||
|
||||
// Domain fragments for hello.soy: Note that this contains no contact info.
|
||||
// Domain fragments for hello.soy.
|
||||
assertThat(domainFrags.stream().findFirst().get().xml().strip())
|
||||
.isEqualTo(
|
||||
"""
|
||||
<rdeDomain:domain>
|
||||
<rdeDomain:name>hello.soy</rdeDomain:name>
|
||||
<rdeDomain:roid>E-SOY</rdeDomain:roid>
|
||||
<rdeDomain:roid>8-SOY</rdeDomain:roid>
|
||||
<rdeDomain:uName>hello.soy</rdeDomain:uName>
|
||||
<rdeDomain:status s="ok"/>
|
||||
<rdeDomain:ns>
|
||||
|
||||
@@ -20,9 +20,8 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadAllOf;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistContactWithPendingTransfer;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistDomainWithDependentResources;
|
||||
import static google.registry.testing.DatabaseHelper.persistDomainWithPendingTransfer;
|
||||
import static google.registry.testing.DatabaseHelper.persistNewRegistrars;
|
||||
@@ -32,10 +31,10 @@ import static org.mockito.Mockito.verify;
|
||||
|
||||
import google.registry.beam.TestPipelineExtension;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
@@ -80,25 +79,13 @@ public class ResaveAllEppResourcesPipelineTest {
|
||||
|
||||
@Test
|
||||
void testPipeline_unchangedEntity() {
|
||||
Contact contact = persistActiveContact("test123");
|
||||
DateTime creationTime = contact.getUpdateTimestamp().getTimestamp();
|
||||
Host host = persistActiveHost("ns1.example.tld");
|
||||
DateTime creationTime = host.getUpdateTimestamp().getTimestamp();
|
||||
fakeClock.advanceOneMilli();
|
||||
assertThat(loadByEntity(contact).getUpdateTimestamp().getTimestamp()).isEqualTo(creationTime);
|
||||
assertThat(loadByEntity(host).getUpdateTimestamp().getTimestamp()).isEqualTo(creationTime);
|
||||
fakeClock.advanceOneMilli();
|
||||
runPipeline();
|
||||
assertThat(loadByEntity(contact)).isEqualTo(contact);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPipeline_fulfilledContactTransfer() {
|
||||
Contact contact = persistActiveContact("test123");
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
contact = persistContactWithPendingTransfer(contact, now, now.plusDays(5), now);
|
||||
fakeClock.advanceBy(Duration.standardDays(10));
|
||||
assertThat(loadByEntity(contact).getStatusValues()).contains(StatusValue.PENDING_TRANSFER);
|
||||
runPipeline();
|
||||
assertThat(loadByEntity(contact).getStatusValues())
|
||||
.doesNotContain(StatusValue.PENDING_TRANSFER);
|
||||
assertThat(loadByEntity(host)).isEqualTo(host);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -108,12 +95,7 @@ public class ResaveAllEppResourcesPipelineTest {
|
||||
Domain domain =
|
||||
persistDomainWithPendingTransfer(
|
||||
persistDomainWithDependentResources(
|
||||
"domain",
|
||||
"tld",
|
||||
persistActiveContact("jd1234"),
|
||||
now.minusDays(5),
|
||||
now.minusDays(5),
|
||||
now.plusYears(2)),
|
||||
"domain", "tld", now.minusDays(5), now.minusDays(5), now.plusYears(2)),
|
||||
now.minusDays(4),
|
||||
now.minusDays(1),
|
||||
now.plusYears(2));
|
||||
@@ -130,8 +112,7 @@ public class ResaveAllEppResourcesPipelineTest {
|
||||
void testPipeline_autorenewedDomain() {
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
Domain domain =
|
||||
persistDomainWithDependentResources(
|
||||
"domain", "tld", persistActiveContact("jd1234"), now, now, now.plusYears(1));
|
||||
persistDomainWithDependentResources("domain", "tld", now, now, now.plusYears(1));
|
||||
assertThat(domain.getRegistrationExpirationTime()).isEqualTo(now.plusYears(1));
|
||||
fakeClock.advanceBy(Duration.standardDays(500));
|
||||
runPipeline();
|
||||
@@ -142,8 +123,7 @@ public class ResaveAllEppResourcesPipelineTest {
|
||||
@Test
|
||||
void testPipeline_expiredGracePeriod() {
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
persistDomainWithDependentResources(
|
||||
"domain", "tld", persistActiveContact("jd1234"), now, now, now.plusYears(1));
|
||||
persistDomainWithDependentResources("domain", "tld", now, now, now.plusYears(1));
|
||||
assertThat(loadAllOf(GracePeriod.class)).hasSize(1);
|
||||
fakeClock.advanceBy(Duration.standardDays(500));
|
||||
runPipeline();
|
||||
@@ -153,8 +133,7 @@ public class ResaveAllEppResourcesPipelineTest {
|
||||
@Test
|
||||
void testPipeline_fastOnlySavesChanged() {
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
Contact contact = persistActiveContact("jd1234");
|
||||
persistDomainWithDependentResources("renewed", "tld", contact, now, now, now.plusYears(1));
|
||||
persistDomainWithDependentResources("renewed", "tld", now, now, now.plusYears(1));
|
||||
persistActiveDomain("nonrenewed.tld", now, now.plusYears(20));
|
||||
// Spy the transaction manager so we can be sure we're only saving the renewed domain
|
||||
JpaTransactionManager spy = spy(tm());
|
||||
@@ -170,24 +149,22 @@ public class ResaveAllEppResourcesPipelineTest {
|
||||
void testPipeline_notFastResavesAll() {
|
||||
options.setFast(false);
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
Contact contact = persistActiveContact("jd1234");
|
||||
Domain renewed =
|
||||
persistDomainWithDependentResources("renewed", "tld", contact, now, now, now.plusYears(1));
|
||||
persistDomainWithDependentResources("renewed", "tld", now, now, now.plusYears(1));
|
||||
Domain nonRenewed =
|
||||
persistDomainWithDependentResources(
|
||||
"nonrenewed", "tld", contact, now, now, now.plusYears(20));
|
||||
persistDomainWithDependentResources("nonrenewed", "tld", now, now, now.plusYears(20));
|
||||
// Spy the transaction manager so we can be sure we're attempting to save everything
|
||||
JpaTransactionManager spy = spy(tm());
|
||||
TransactionManagerFactory.setJpaTm(() -> spy);
|
||||
ArgumentCaptor<EppResource> eppResourcePutCaptor = ArgumentCaptor.forClass(EppResource.class);
|
||||
runPipeline();
|
||||
// We should be attempting to put both domains (and the contact) in, even the unchanged ones
|
||||
verify(spy, times(3)).put(eppResourcePutCaptor.capture());
|
||||
// We should be attempting to put both domains in, even the unchanged one
|
||||
verify(spy, times(2)).put(eppResourcePutCaptor.capture());
|
||||
assertThat(
|
||||
eppResourcePutCaptor.getAllValues().stream()
|
||||
.map(EppResource::getRepoId)
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly(contact.getRepoId(), renewed.getRepoId(), nonRenewed.getRepoId());
|
||||
.containsExactly(renewed.getRepoId(), nonRenewed.getRepoId());
|
||||
}
|
||||
|
||||
private void runPipeline() {
|
||||
|
||||
@@ -20,7 +20,6 @@ import static google.registry.model.ImmutableObjectSubject.immutableObjectCorres
|
||||
import static google.registry.persistence.transaction.JpaTransactionManagerExtension.makeRegistrar1;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistNewRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@@ -37,7 +36,6 @@ import com.google.common.truth.Correspondence.BinaryPredicate;
|
||||
import google.registry.beam.TestPipelineExtension;
|
||||
import google.registry.beam.spec11.SafeBrowsingTransforms.EvaluateSafeBrowsingFn;
|
||||
import google.registry.beam.spec11.SafeBrowsingTransformsTest.HttpResponder;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainAuthInfo;
|
||||
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
|
||||
@@ -54,7 +52,6 @@ import google.registry.util.Retrier;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import org.apache.beam.sdk.coders.KvCoder;
|
||||
import org.apache.beam.sdk.coders.SerializableCoder;
|
||||
import org.apache.beam.sdk.options.PipelineOptionsFactory;
|
||||
@@ -252,16 +249,11 @@ class Spec11PipelineTest {
|
||||
createTld("bank");
|
||||
createTld("dev");
|
||||
|
||||
Contact contact1 = persistActiveContact(registrar1.getRegistrarId());
|
||||
Contact contact2 = persistActiveContact(registrar2.getRegistrarId());
|
||||
Contact contact3 = persistActiveContact(registrar3.getRegistrarId());
|
||||
|
||||
persistResource(createDomain("111.com", "123456789-COM", registrar1, contact1));
|
||||
persistResource(createDomain("party-night.net", "2244AABBC-NET", registrar2, contact2));
|
||||
persistResource(createDomain("bitcoin.bank", "1C3D5E7F9-BANK", registrar1, contact1));
|
||||
persistResource(createDomain("no-email.com", "2A4BA9BBC-COM", registrar2, contact2));
|
||||
persistResource(
|
||||
createDomain("anti-anti-anti-virus.dev", "555666888-DEV", registrar3, contact3));
|
||||
persistResource(createDomain("111.com", "123456789-COM", registrar1));
|
||||
persistResource(createDomain("party-night.net", "2244AABBC-NET", registrar2));
|
||||
persistResource(createDomain("bitcoin.bank", "1C3D5E7F9-BANK", registrar1));
|
||||
persistResource(createDomain("no-email.com", "2A4BA9BBC-COM", registrar2));
|
||||
persistResource(createDomain("anti-anti-anti-virus.dev", "555666888-DEV", registrar3));
|
||||
}
|
||||
|
||||
private void verifySaveToGcs() throws Exception {
|
||||
@@ -289,8 +281,7 @@ class Spec11PipelineTest {
|
||||
});
|
||||
}
|
||||
|
||||
private Domain createDomain(
|
||||
String domainName, String repoId, Registrar registrar, Contact contact) {
|
||||
private Domain createDomain(String domainName, String repoId, Registrar registrar) {
|
||||
return new Domain.Builder()
|
||||
.setDomainName(domainName)
|
||||
.setRepoId(repoId)
|
||||
@@ -298,7 +289,6 @@ class Spec11PipelineTest {
|
||||
.setLastEppUpdateTime(fakeClock.nowUtc())
|
||||
.setLastEppUpdateRegistrarId(registrar.getRegistrarId())
|
||||
.setLastTransferTime(fakeClock.nowUtc())
|
||||
.setRegistrant(Optional.of(contact.createVKey()))
|
||||
.setPersistedCurrentSponsorRegistrarId(registrar.getRegistrarId())
|
||||
.setRegistrationExpirationTime(fakeClock.nowUtc().plusYears(1))
|
||||
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("password")))
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
// 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.beam.wipeout;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.CONTACT_CREATE;
|
||||
import static google.registry.persistence.PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ;
|
||||
import static google.registry.testing.DatabaseHelper.loadAllOf;
|
||||
import static google.registry.testing.DatabaseHelper.newContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.hibernate.cfg.AvailableSettings.ISOLATION;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.beam.TestPipelineExtension;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.contact.ContactPhoneNumber;
|
||||
import google.registry.model.reporting.HistoryEntryDao;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import org.apache.beam.sdk.options.PipelineOptionsFactory;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link WipeOutContactHistoryPiiPipeline}. */
|
||||
public class WipeOutContactHistoryPiiPipelineTest {
|
||||
|
||||
private static final int MIN_AGE_IN_MONTHS = 18;
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER =
|
||||
DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
|
||||
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2020-02-02T12:34:56Z"));
|
||||
private final WipeOutContactHistoryPiiPipelineOptions options =
|
||||
PipelineOptionsFactory.create().as(WipeOutContactHistoryPiiPipelineOptions.class);
|
||||
private Contact contact1;
|
||||
private Contact contact2;
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder()
|
||||
.withClock(clock)
|
||||
.withProperty(ISOLATION, TRANSACTION_REPEATABLE_READ.name())
|
||||
.buildIntegrationTestExtension();
|
||||
|
||||
@RegisterExtension
|
||||
final TestPipelineExtension pipeline =
|
||||
TestPipelineExtension.create().enableAbandonedNodeEnforcement(true);
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
contact1 =
|
||||
persistResource(
|
||||
newContact("my-contact1")
|
||||
.asBuilder()
|
||||
.setEmailAddress("test@example.com")
|
||||
.setFaxNumber(
|
||||
new ContactPhoneNumber.Builder().setPhoneNumber("+12122122122").build())
|
||||
.build());
|
||||
contact2 =
|
||||
persistResource(
|
||||
newContact("my-contact2")
|
||||
.asBuilder()
|
||||
.setEmailAddress("test@example.tld")
|
||||
.setVoiceNumber(
|
||||
new ContactPhoneNumber.Builder().setPhoneNumber("+19177199177").build())
|
||||
.build());
|
||||
// T = 0 month;
|
||||
persistResource(createHistory(contact1));
|
||||
// T = 5 months;
|
||||
advanceMonths(5);
|
||||
persistResource(createHistory(contact2));
|
||||
// T = 10 months;
|
||||
advanceMonths(5);
|
||||
persistResource(createHistory(contact1));
|
||||
persistResource(createHistory(contact2));
|
||||
// T = 20 months;
|
||||
advanceMonths(10);
|
||||
persistResource(createHistory(contact2));
|
||||
// T = 30 months;
|
||||
advanceMonths(10);
|
||||
options.setCutoffTime(DATE_TIME_FORMATTER.print(clock.nowUtc().minusMonths(MIN_AGE_IN_MONTHS)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess() {
|
||||
// Before the pipeline runs, every history entry should have an emali address.
|
||||
assertThat(
|
||||
loadAllOf(ContactHistory.class).stream()
|
||||
.filter(e -> e.getContactBase().get().getEmailAddress() != null)
|
||||
.count())
|
||||
.isEqualTo(5);
|
||||
// Before the pipeline runs, contact history for contact1 should have fax numbers.
|
||||
ImmutableList<ContactHistory> histories =
|
||||
HistoryEntryDao.loadHistoryObjectsForResource(contact1.createVKey(), ContactHistory.class);
|
||||
assertThat(
|
||||
histories.stream().filter(e -> e.getContactBase().get().getFaxNumber() != null).count())
|
||||
.isEqualTo(2);
|
||||
// Before the pipeline runs, contact history for contact2 should have voice numbers.
|
||||
histories =
|
||||
HistoryEntryDao.loadHistoryObjectsForResource(contact2.createVKey(), ContactHistory.class);
|
||||
assertThat(
|
||||
histories.stream()
|
||||
.filter(e -> e.getContactBase().get().getVoiceNumber() != null)
|
||||
.count())
|
||||
.isEqualTo(3);
|
||||
WipeOutContactHistoryPiiPipeline wipeOutContactHistoryPiiPipeline =
|
||||
new WipeOutContactHistoryPiiPipeline(options);
|
||||
wipeOutContactHistoryPiiPipeline.run(pipeline).waitUntilFinish();
|
||||
histories =
|
||||
HistoryEntryDao.loadHistoryObjectsForResource(contact1.createVKey(), ContactHistory.class);
|
||||
assertThat(histories.size()).isEqualTo(2);
|
||||
ImmutableList<ContactHistory> wipedEntries =
|
||||
histories.stream()
|
||||
.filter(e -> e.getContactBase().get().getEmailAddress() == null)
|
||||
.collect(toImmutableList());
|
||||
// Only the history entry at T = 10 is wiped. The one at T = 10 is over 18 months old, but it
|
||||
// is the most recent entry, so it is kept.
|
||||
assertThat(wipedEntries.size()).isEqualTo(1);
|
||||
assertThat(wipedEntries.get(0).getContactBase().get().getFaxNumber()).isNull();
|
||||
// With a new history entry at T = 30, the one at T = 10 is eligible for wipe out. Note the
|
||||
// current time itself (therefore the cutoff time) has not changed.
|
||||
persistResource(createHistory(contact1));
|
||||
wipeOutContactHistoryPiiPipeline.run(pipeline).waitUntilFinish();
|
||||
histories =
|
||||
HistoryEntryDao.loadHistoryObjectsForResource(contact1.createVKey(), ContactHistory.class);
|
||||
assertThat(histories.size()).isEqualTo(3);
|
||||
wipedEntries =
|
||||
histories.stream()
|
||||
.filter(e -> e.getContactBase().get().getEmailAddress() == null)
|
||||
.collect(toImmutableList());
|
||||
assertThat(wipedEntries.size()).isEqualTo(2);
|
||||
// Check that the pipeline deals with multiple contacts correctly.
|
||||
histories =
|
||||
HistoryEntryDao.loadHistoryObjectsForResource(contact2.createVKey(), ContactHistory.class);
|
||||
assertThat(histories.size()).isEqualTo(3);
|
||||
wipedEntries =
|
||||
histories.stream()
|
||||
.filter(e -> e.getContactBase().get().getEmailAddress() == null)
|
||||
.collect(toImmutableList());
|
||||
// Only the history entry at T = 10 is wiped. The one at T = 10 is over 18 months old, but it
|
||||
// is the most recent entry, so it is kept.
|
||||
assertThat(wipedEntries.size()).isEqualTo(2);
|
||||
assertThat(wipedEntries.get(0).getContactBase().get().getVoiceNumber()).isNull();
|
||||
assertThat(wipedEntries.get(1).getContactBase().get().getVoiceNumber()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_dryRun() {
|
||||
options.setIsDryRun(true);
|
||||
WipeOutContactHistoryPiiPipeline wipeOutContactHistoryPiiPipeline =
|
||||
new WipeOutContactHistoryPiiPipeline(options);
|
||||
wipeOutContactHistoryPiiPipeline.run(pipeline).waitUntilFinish();
|
||||
ImmutableList<ContactHistory> histories =
|
||||
HistoryEntryDao.loadHistoryObjectsForResource(contact1.createVKey(), ContactHistory.class);
|
||||
assertThat(histories.size()).isEqualTo(2);
|
||||
assertThat(
|
||||
histories.stream()
|
||||
.filter(e -> e.getContactBase().get().getEmailAddress() == null)
|
||||
.collect(toImmutableList()))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
private ContactHistory createHistory(Contact contact) {
|
||||
return new ContactHistory.Builder()
|
||||
.setContact(contact)
|
||||
.setType(CONTACT_CREATE)
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setModificationTime(clock.nowUtc())
|
||||
.build();
|
||||
}
|
||||
|
||||
private void advanceMonths(int months) {
|
||||
DateTime now = clock.nowUtc();
|
||||
DateTime next = now.plusMonths(months);
|
||||
clock.advanceBy(new Duration(now, next));
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableO
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadAllOf;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
@@ -86,8 +85,6 @@ class EppPointInTimeTest {
|
||||
|
||||
persistActiveHost("ns1.example.net");
|
||||
persistActiveHost("ns2.example.net");
|
||||
persistActiveContact("jd1234");
|
||||
persistActiveContact("sh8013");
|
||||
|
||||
clock.advanceBy(standardDays(1));
|
||||
DateTime timeAtCreate = clock.nowUtc();
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
// Copyright 2026 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;
|
||||
|
||||
package google.registry.flows;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.flows.FeeExtensionXmlTagNormalizer.feeExtensionInUseRegex;
|
||||
import static google.registry.flows.FeeExtensionXmlTagNormalizer.normalize;
|
||||
import static google.registry.flows.FlowTestCase.verifyFeeTagNormalized;
|
||||
import static google.registry.model.eppcommon.EppXmlTransformer.validateOutput;
|
||||
import static google.registry.testing.TestDataHelper.loadFile;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
class FeeExtensionXmlTagNormalizerTest {
|
||||
|
||||
@Test
|
||||
void feeExtensionInUseRegex_correct() {
|
||||
assertThat(feeExtensionInUseRegex())
|
||||
.isEqualTo("\\b(fee):|\\b(fee11):|\\b(fee12):|\\b(fee_1_00):");
|
||||
}
|
||||
|
||||
@Test
|
||||
void normalize_noFeeExtensions() throws Exception {
|
||||
String xml = loadFile(getClass(), "domain_create.xml");
|
||||
String normalized = normalize(xml);
|
||||
assertThat(normalized).isEqualTo(xml);
|
||||
}
|
||||
|
||||
@Test
|
||||
void normalize_greetingUnchanged() throws Exception {
|
||||
String xml = loadFile(getClass(), "greeting.xml");
|
||||
String normalized = normalize(xml);
|
||||
assertThat(normalized).isEqualTo(xml);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "normalize_withFeeExtension-{0}")
|
||||
@MethodSource("provideTestCombinations")
|
||||
@SuppressWarnings("unused") // Parameter 'name' is part of test case name
|
||||
void normalize_withFeeExtension(String name, String inputXmlFilename, String expectedXmlFilename)
|
||||
throws Exception {
|
||||
String original = loadFile(getClass(), inputXmlFilename);
|
||||
String normalized = normalize(original);
|
||||
String expected = loadFile(getClass(), expectedXmlFilename);
|
||||
// Verify that expected xml is syntatically correct.
|
||||
validateOutput(expected);
|
||||
|
||||
assertThat(normalized).isEqualTo(expected);
|
||||
}
|
||||
|
||||
// Piggyback tests for FlowTestCase.verifyFeeTagNormalized here.
|
||||
@ParameterizedTest(name = "verifyFeeTagNormalized-{0}")
|
||||
@MethodSource("provideTestCombinations")
|
||||
@SuppressWarnings("unused") // Parameter 'name' is part of test case name
|
||||
void verifyFeeTagNormalized_success(
|
||||
String name, String inputXmlFilename, String expectedXmlFilename) throws Exception {
|
||||
String original = loadFile(getClass(), inputXmlFilename);
|
||||
String expected = loadFile(getClass(), expectedXmlFilename);
|
||||
|
||||
if (name.equals("v06")) {
|
||||
// Fee-06 already uses 'fee'. Non-normalized tags only appear in header.
|
||||
verifyFeeTagNormalized(original);
|
||||
} else {
|
||||
assertThrows(
|
||||
AssertionError.class,
|
||||
() -> {
|
||||
verifyFeeTagNormalized(original);
|
||||
});
|
||||
}
|
||||
verifyFeeTagNormalized(expected);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static Stream<Arguments> provideTestCombinations() {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
"v06",
|
||||
"domain_check_fee_response_raw_v06.xml",
|
||||
"domain_check_fee_response_normalized_v06.xml"),
|
||||
Arguments.of(
|
||||
"v11",
|
||||
"domain_check_fee_response_raw_v11.xml",
|
||||
"domain_check_fee_response_normalized_v11.xml"),
|
||||
Arguments.of(
|
||||
"v12",
|
||||
"domain_check_fee_response_raw_v12.xml",
|
||||
"domain_check_fee_response_normalized_v12.xml"),
|
||||
Arguments.of(
|
||||
"stdv1",
|
||||
"domain_check_fee_response_raw_stdv1.xml",
|
||||
"domain_check_fee_response_normalized_stdv1.xml"));
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static google.registry.flows.FlowUtils.marshalWithLenientRetry;
|
||||
import static google.registry.model.eppcommon.EppXmlTransformer.marshal;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.stripBillingEventId;
|
||||
@@ -55,6 +56,7 @@ import google.registry.xml.ValidationMode;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -135,28 +137,6 @@ public abstract class FlowTestCase<F extends Flow> {
|
||||
return TestDataHelper.loadFile(getClass(), filename, substitutions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an input or response EPP message with draft fee extension v12 to std v1.
|
||||
*
|
||||
* <p>There is no practical changes between draft v12 and the v1 standard. This method allows us
|
||||
* to reuse v12 test data.
|
||||
*/
|
||||
protected String loadFeeV12FileAsStdV1(String filename) {
|
||||
String content = loadFile(filename);
|
||||
return content.replace("urn:ietf:params:xml:ns:fee-0.12", "urn:ietf:params:xml:ns:epp:fee-1.0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an input or response EPP message with draft fee extension v12 to std v1.
|
||||
*
|
||||
* <p>There is no practical changes between draft v12 and the v1 standard. This method allows us
|
||||
* to reuse v12 test data.
|
||||
*/
|
||||
protected String loadFeeV12FileAsStdV1(String filename, Map<String, String> substitutions) {
|
||||
String content = loadFile(filename, substitutions);
|
||||
return content.replace("urn:ietf:params:xml:ns:fee-0.12", "urn:ietf:params:xml:ns:epp:fee-1.0");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected String getClientTrid() throws Exception {
|
||||
return eppLoader.getEpp().getCommandWrapper().getClTrid().orElse(null);
|
||||
@@ -306,6 +286,7 @@ public abstract class FlowTestCase<F extends Flow> {
|
||||
Arrays.toString(marshal(output, ValidationMode.LENIENT))),
|
||||
e);
|
||||
}
|
||||
verifyFeeTagNormalized(new String(marshalWithLenientRetry(output), UTF_8));
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -320,4 +301,14 @@ public abstract class FlowTestCase<F extends Flow> {
|
||||
public EppOutput runFlowAssertResponse(String xml, String... ignoredPaths) throws Exception {
|
||||
return runFlowAssertResponse(CommitMode.LIVE, UserPrivileges.NORMAL, xml, ignoredPaths);
|
||||
}
|
||||
|
||||
// Pattern for non-normalized tags in use. Occurrences in namespace declarations ignored.
|
||||
private static final Pattern NON_NORMALIZED_FEE_TAGS =
|
||||
Pattern.compile("\\bfee11:|\\bfee12:|\\bfee_1_00:");
|
||||
|
||||
static void verifyFeeTagNormalized(String xml) {
|
||||
assertWithMessage("Unexpected un-normalized Fee tags found in message.")
|
||||
.that(NON_NORMALIZED_FEE_TAGS.matcher(xml).find())
|
||||
.isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,13 +24,10 @@ import com.google.common.collect.Iterables;
|
||||
import com.google.common.testing.TestLogHandler;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.contact.ContactBase;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.eppinput.EppInput.ResourceCommandWrapper;
|
||||
import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.host.HostBase;
|
||||
import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.tmch.ClaimsList;
|
||||
@@ -131,18 +128,12 @@ public abstract class ResourceFlowTestCase<F extends Flow, R extends EppResource
|
||||
|
||||
protected void assertLastHistoryContainsResource(EppResource resource) {
|
||||
HistoryEntry historyEntry = Iterables.getLast(DatabaseHelper.getHistoryEntries(resource));
|
||||
if (resource instanceof ContactBase) {
|
||||
ContactHistory contactHistory = (ContactHistory) historyEntry;
|
||||
// Don't use direct equals comparison since one might be a subclass of the other
|
||||
assertAboutImmutableObjects()
|
||||
.that(contactHistory.getContactBase().get())
|
||||
.hasFieldsEqualTo(resource);
|
||||
} else if (resource instanceof DomainBase) {
|
||||
if (resource instanceof DomainBase) {
|
||||
DomainHistory domainHistory = (DomainHistory) historyEntry;
|
||||
assertAboutImmutableObjects()
|
||||
.that(domainHistory.getDomainBase().get())
|
||||
.isEqualExceptFields(resource, "gracePeriods", "dsData", "nsHosts");
|
||||
} else if (resource instanceof HostBase) {
|
||||
} else {
|
||||
HostHistory hostHistory = (HostHistory) historyEntry;
|
||||
// Don't use direct equals comparison since one might be a subclass of the other
|
||||
assertAboutImmutableObjects()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -44,7 +44,6 @@ import static google.registry.testing.DatabaseHelper.loadByKeyIfPresent;
|
||||
import static google.registry.testing.DatabaseHelper.loadByKeysIfPresent;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.newHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistDeletedDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
@@ -81,7 +80,6 @@ import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingCancellation;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingRecurrence;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
@@ -104,7 +102,6 @@ import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.LogsSubject;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -123,11 +120,13 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
private static final DateTime A_MONTH_FROM_NOW = TIME_BEFORE_FLOW.plusMonths(1);
|
||||
|
||||
private static final ImmutableMap<String, String> FEE_06_MAP =
|
||||
ImmutableMap.of("FEE_VERSION", "0.6", "FEE_NS", "fee");
|
||||
ImmutableMap.of("FEE_VERSION", "fee-0.6", "FEE_NS", "fee");
|
||||
private static final ImmutableMap<String, String> FEE_11_MAP =
|
||||
ImmutableMap.of("FEE_VERSION", "0.11", "FEE_NS", "fee11");
|
||||
ImmutableMap.of("FEE_VERSION", "fee-0.11", "FEE_NS", "fee11");
|
||||
private static final ImmutableMap<String, String> FEE_12_MAP =
|
||||
ImmutableMap.of("FEE_VERSION", "0.12", "FEE_NS", "fee12");
|
||||
ImmutableMap.of("FEE_VERSION", "fee-0.12", "FEE_NS", "fee12");
|
||||
private static final ImmutableMap<String, String> FEE_STD_1_0_MAP =
|
||||
ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "FEE_NS", "fee1_00");
|
||||
|
||||
DomainDeleteFlowTest() {
|
||||
setEppInput("domain_delete.xml");
|
||||
@@ -158,14 +157,11 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
}
|
||||
|
||||
private void createReferencedEntities(DateTime expirationTime) throws Exception {
|
||||
// Persist a linked contact.
|
||||
Contact contact = persistActiveContact("sh8013");
|
||||
domain =
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setCreationTimeForTest(TIME_BEFORE_FLOW)
|
||||
.setRegistrant(Optional.of(contact.createVKey()))
|
||||
.setRegistrationExpirationTime(expirationTime)
|
||||
.build());
|
||||
earlierHistoryEntry =
|
||||
@@ -396,24 +392,9 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_addGracePeriodCredit_v06() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
doAddGracePeriodDeleteTest(GracePeriodStatus.ADD, "domain_delete_response_fee.xml", FEE_06_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_addGracePeriodCredit_v11() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
doAddGracePeriodDeleteTest(GracePeriodStatus.ADD, "domain_delete_response_fee.xml", FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_addGracePeriodCredit_v12() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
doAddGracePeriodDeleteTest(GracePeriodStatus.ADD, "domain_delete_response_fee.xml", FEE_12_MAP);
|
||||
void testSuccess_addGracePeriodCredit_std_v1() throws Exception {
|
||||
doAddGracePeriodDeleteTest(
|
||||
GracePeriodStatus.ADD, "domain_delete_response_fee.xml", FEE_STD_1_0_MAP);
|
||||
}
|
||||
|
||||
private void doSuccessfulTest_noAddGracePeriod(String responseFilename) throws Exception {
|
||||
@@ -497,24 +478,8 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_renewGracePeriodCredit_v06() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending_fee.xml", FEE_06_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_renewGracePeriodCredit_v11() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending_fee.xml", FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_renewGracePeriodCredit_v12() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending_fee.xml", FEE_12_MAP);
|
||||
void testSuccess_renewGracePeriodCredit_std_v1() throws Exception {
|
||||
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending_fee.xml", FEE_STD_1_0_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -556,37 +521,14 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_autoRenewGracePeriod_v06() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
void testSuccess_autoRenewGracePeriod_std_v1() throws Exception {
|
||||
setUpAutorenewGracePeriod();
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_06_MAP));
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_STD_1_0_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_autoRenewGracePeriod_v11() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
setUpAutorenewGracePeriod();
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_11_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_autoRenewGracePeriod_v12() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
setUpAutorenewGracePeriod();
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_12_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_autoRenewGracePeriod_priceChanges_v06() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
void testSuccess_autoRenewGracePeriod_priceChanges_std_v1() throws Exception {
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
@@ -599,44 +541,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
.build());
|
||||
setUpAutorenewGracePeriod();
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_06_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_autoRenewGracePeriod_priceChanges_v11() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setRenewBillingCostTransitions(
|
||||
ImmutableSortedMap.of(
|
||||
START_OF_TIME,
|
||||
Money.of(USD, 11),
|
||||
TIME_BEFORE_FLOW.minusDays(5),
|
||||
Money.of(USD, 20)))
|
||||
.build());
|
||||
setUpAutorenewGracePeriod();
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_11_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_autoRenewGracePeriod_priceChanges_v12() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setRenewBillingCostTransitions(
|
||||
ImmutableSortedMap.of(
|
||||
START_OF_TIME,
|
||||
Money.of(USD, 11),
|
||||
TIME_BEFORE_FLOW.minusDays(5),
|
||||
Money.of(USD, 20)))
|
||||
.build());
|
||||
setUpAutorenewGracePeriod();
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_12_MAP));
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_STD_1_0_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -748,11 +653,10 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
.asBuilder()
|
||||
.setNameservers(ImmutableSet.of(host.createVKey()))
|
||||
.build());
|
||||
// Persist another domain that's already been deleted and references this contact and host.
|
||||
// Persist another domain that's already been deleted and references this host.
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain("example1.tld")
|
||||
.asBuilder()
|
||||
.setRegistrant(ForeignKeyUtils.loadKey(Contact.class, "sh8013", clock.nowUtc()))
|
||||
.setNameservers(ImmutableSet.of(host.createVKey()))
|
||||
.setDeletionTime(START_OF_TIME)
|
||||
.build());
|
||||
@@ -851,7 +755,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
.setEventTime(now)
|
||||
.setMsg(
|
||||
"Domain example.tld was deleted by registry administrator with final deletion"
|
||||
+ " effective: 2000-07-11T22:00:00.012Z")
|
||||
+ " effective: 2000-07-11T22:00:00.010Z")
|
||||
.setResponseData(
|
||||
ImmutableList.of(
|
||||
DomainPendingActionNotificationResponse.create(
|
||||
@@ -860,7 +764,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
new PollMessage.OneTime.Builder()
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setHistoryEntry(deleteHistoryEntry)
|
||||
.setEventTime(DateTime.parse("2000-07-11T22:00:00.012Z"))
|
||||
.setEventTime(DateTime.parse("2000-07-11T22:00:00.010Z"))
|
||||
.setMsg("Deleted by registry administrator.")
|
||||
.setResponseData(
|
||||
ImmutableList.of(
|
||||
@@ -868,7 +772,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
"example.tld",
|
||||
true,
|
||||
deleteHistoryEntry.getTrid(),
|
||||
DateTime.parse("2000-07-11T22:00:00.012Z"))))
|
||||
DateTime.parse("2000-07-11T22:00:00.010Z"))))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -1296,7 +1200,6 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
|
||||
@Test
|
||||
void testSuccess_freeCreation_deletionDuringGracePeriod() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
// Deletion during the add grace period should still work even if the credit is 0
|
||||
setUpSuccessfulTest();
|
||||
BillingEvent graceBillingEvent =
|
||||
@@ -1304,7 +1207,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
setUpGracePeriods(
|
||||
GracePeriod.forBillingEvent(GracePeriodStatus.ADD, domain.getRepoId(), graceBillingEvent));
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_fee_free_grace.xml"));
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_fee_free_grace_stdv1.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1330,4 +1233,144 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
+ "history_registrar_id,history_modification_time,history_other_registrar_id,"
|
||||
+ "history_period_unit,history_period_value,history_reason,history");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_renewGracePeriodCredit_v06() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending_fee.xml", FEE_06_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_renewGracePeriodCredit_v11() throws Exception {
|
||||
setEppInput("domain_delete.xml", FEE_11_MAP);
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending_fee.xml", FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_renewGracePeriodCredit_v12() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending_fee.xml", FEE_12_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_addGracePeriodCredit_v06() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
doAddGracePeriodDeleteTest(GracePeriodStatus.ADD, "domain_delete_response_fee.xml", FEE_06_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_addGracePeriodCredit_v11() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
doAddGracePeriodDeleteTest(GracePeriodStatus.ADD, "domain_delete_response_fee.xml", FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_addGracePeriodCredit_v12() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
doAddGracePeriodDeleteTest(GracePeriodStatus.ADD, "domain_delete_response_fee.xml", FEE_12_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_autoRenewGracePeriod_v06() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
setUpAutorenewGracePeriod();
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_06_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_autoRenewGracePeriod_v11() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
setUpAutorenewGracePeriod();
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_11_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_autoRenewGracePeriod_v12() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
setUpAutorenewGracePeriod();
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_12_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_autoRenewGracePeriod_priceChanges_v06() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setRenewBillingCostTransitions(
|
||||
ImmutableSortedMap.of(
|
||||
START_OF_TIME,
|
||||
Money.of(USD, 11),
|
||||
TIME_BEFORE_FLOW.minusDays(5),
|
||||
Money.of(USD, 20)))
|
||||
.build());
|
||||
setUpAutorenewGracePeriod();
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_06_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_autoRenewGracePeriod_priceChanges_v11() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setRenewBillingCostTransitions(
|
||||
ImmutableSortedMap.of(
|
||||
START_OF_TIME,
|
||||
Money.of(USD, 11),
|
||||
TIME_BEFORE_FLOW.minusDays(5),
|
||||
Money.of(USD, 20)))
|
||||
.build());
|
||||
setUpAutorenewGracePeriod();
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_11_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_autoRenewGracePeriod_priceChanges_v12() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setRenewBillingCostTransitions(
|
||||
ImmutableSortedMap.of(
|
||||
START_OF_TIME,
|
||||
Money.of(USD, 11),
|
||||
TIME_BEFORE_FLOW.minusDays(5),
|
||||
Money.of(USD, 20)))
|
||||
.build());
|
||||
setUpAutorenewGracePeriod();
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_12_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_freeCreation_deletionDuringGracePeriod_v12() throws Exception {
|
||||
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
|
||||
// Deletion during the add grace period should still work even if the credit is 0
|
||||
setUpSuccessfulTest();
|
||||
BillingEvent graceBillingEvent =
|
||||
persistResource(createBillingEvent(Reason.CREATE, Money.of(USD, 0)));
|
||||
setUpGracePeriods(
|
||||
GracePeriod.forBillingEvent(GracePeriodStatus.ADD, domain.getRepoId(), graceBillingEvent));
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_fee_free_grace_v12.xml"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,6 +586,115 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIcannActivityReportField_getsLogged() throws Exception {
|
||||
persistTestEntities(false);
|
||||
runFlow();
|
||||
assertIcannReportingActivityFieldLogged("srs-dom-info");
|
||||
assertTldsFieldLogged("tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBulkInfoExtension_returnsBulkInfo() throws Exception {
|
||||
persistTestEntities(false);
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
domain = domain.asBuilder().setCurrentBulkToken(token.createVKey()).build();
|
||||
persistResource(domain);
|
||||
setEppInput("domain_info_bulk.xml");
|
||||
doSuccessfulTest("domain_info_response_bulk.xml", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBulkInfoExtension_returnsBulkInfoForSuperUser() throws Exception {
|
||||
persistTestEntities(false);
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
domain = domain.asBuilder().setCurrentBulkToken(token.createVKey()).build();
|
||||
persistResource(domain);
|
||||
sessionMetadata.setRegistrarId("TheRegistrar");
|
||||
setEppInput("domain_info_bulk.xml");
|
||||
EppOutput output = runFlowAsSuperuser();
|
||||
String expectedOutput =
|
||||
loadFile(
|
||||
"domain_info_response_superuser_bulk.xml",
|
||||
updateSubstitutions(ImmutableMap.of(), "ROID", "2FF-TLD"));
|
||||
assertXmlEquals(expectedOutput, new String(marshal(output, ValidationMode.LENIENT), UTF_8));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBulkInfoExtension_nameNotInBulkPackage() throws Exception {
|
||||
setEppInput("domain_info_bulk.xml");
|
||||
doSuccessfulTest("domain_info_response_empty_bulk_package.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBulkInfoExtension_notCurrentSponsorRegistrar() throws Exception {
|
||||
persistTestEntities(false);
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
domain = domain.asBuilder().setCurrentBulkToken(token.createVKey()).build();
|
||||
persistResource(domain);
|
||||
sessionMetadata.setRegistrarId("TheRegistrar");
|
||||
setEppInput("domain_info_bulk.xml");
|
||||
doSuccessfulTest("domain_info_response_unauthorized.xml", false);
|
||||
}
|
||||
|
||||
// The fee extension is no longer supported in domain:info commands as of version 1.0.
|
||||
// For now, we still support old versions.
|
||||
@Test
|
||||
void testFeeExtension_restoreCommand_pendingDelete_withRenewal() throws Exception {
|
||||
createTld("example");
|
||||
setEppInput(
|
||||
"domain_info_fee.xml",
|
||||
updateSubstitutions(
|
||||
SUBSTITUTION_BASE, "NAME", "rich.example", "COMMAND", "restore", "PERIOD", "1"));
|
||||
persistTestEntities("rich.example", false);
|
||||
setUpBillingEventForExistingDomain();
|
||||
persistResource(
|
||||
domain
|
||||
.asBuilder()
|
||||
.setDeletionTime(clock.nowUtc().plusDays(25))
|
||||
.setRegistrationExpirationTime(clock.nowUtc().minusDays(1))
|
||||
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
|
||||
.build());
|
||||
doSuccessfulTest(
|
||||
"domain_info_fee_restore_response_with_renewal.xml", false, ImmutableMap.of(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test create command. Fee extension version 6 is the only one which supports fee extensions on
|
||||
* info commands and responses, so we don't need to test the other versions.
|
||||
@@ -674,26 +783,6 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
"domain_info_fee_restore_response_no_renewal.xml", false, ImmutableMap.of(), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFeeExtension_restoreCommand_pendingDelete_withRenewal() throws Exception {
|
||||
createTld("example");
|
||||
setEppInput(
|
||||
"domain_info_fee.xml",
|
||||
updateSubstitutions(
|
||||
SUBSTITUTION_BASE, "NAME", "rich.example", "COMMAND", "restore", "PERIOD", "1"));
|
||||
persistTestEntities("rich.example", false);
|
||||
setUpBillingEventForExistingDomain();
|
||||
persistResource(
|
||||
domain
|
||||
.asBuilder()
|
||||
.setDeletionTime(clock.nowUtc().plusDays(25))
|
||||
.setRegistrationExpirationTime(clock.nowUtc().minusDays(1))
|
||||
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
|
||||
.build());
|
||||
doSuccessfulTest(
|
||||
"domain_info_fee_restore_response_with_renewal.xml", false, ImmutableMap.of(), true);
|
||||
}
|
||||
|
||||
/** Test create command on a premium label. */
|
||||
@Test
|
||||
void testFeeExtension_createCommandPremium() throws Exception {
|
||||
@@ -867,16 +956,6 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFeeExtension_unknownCurrency() {
|
||||
setEppInput(
|
||||
"domain_info_fee.xml",
|
||||
updateSubstitutions(
|
||||
SUBSTITUTION_BASE, "COMMAND", "create", "CURRENCY", "BAD", "PERIOD", "1"));
|
||||
EppException thrown = assertThrows(UnknownCurrencyEppException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
/** Test requesting a period that isn't in years. */
|
||||
@Test
|
||||
void testFeeExtension_periodNotInYears() {
|
||||
@@ -934,89 +1013,12 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIcannActivityReportField_getsLogged() throws Exception {
|
||||
persistTestEntities(false);
|
||||
runFlow();
|
||||
assertIcannReportingActivityFieldLogged("srs-dom-info");
|
||||
assertTldsFieldLogged("tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBulkInfoExtension_returnsBulkInfo() throws Exception {
|
||||
persistTestEntities(false);
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
domain = domain.asBuilder().setCurrentBulkToken(token.createVKey()).build();
|
||||
persistResource(domain);
|
||||
setEppInput("domain_info_bulk.xml");
|
||||
doSuccessfulTest("domain_info_response_bulk.xml", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBulkInfoExtension_returnsBulkInfoForSuperUser() throws Exception {
|
||||
persistTestEntities(false);
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
domain = domain.asBuilder().setCurrentBulkToken(token.createVKey()).build();
|
||||
persistResource(domain);
|
||||
sessionMetadata.setRegistrarId("TheRegistrar");
|
||||
setEppInput("domain_info_bulk.xml");
|
||||
EppOutput output = runFlowAsSuperuser();
|
||||
String expectedOutput =
|
||||
loadFile(
|
||||
"domain_info_response_superuser_bulk.xml",
|
||||
updateSubstitutions(ImmutableMap.of(), "ROID", "2FF-TLD"));
|
||||
assertXmlEquals(expectedOutput, new String(marshal(output, ValidationMode.LENIENT), UTF_8));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBulkInfoExtension_nameNotInBulkPackage() throws Exception {
|
||||
setEppInput("domain_info_bulk.xml");
|
||||
doSuccessfulTest("domain_info_response_empty_bulk_package.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBulkInfoExtension_notCurrentSponsorRegistrar() throws Exception {
|
||||
persistTestEntities(false);
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
domain = domain.asBuilder().setCurrentBulkToken(token.createVKey()).build();
|
||||
persistResource(domain);
|
||||
sessionMetadata.setRegistrarId("TheRegistrar");
|
||||
setEppInput("domain_info_bulk.xml");
|
||||
doSuccessfulTest("domain_info_response_unauthorized.xml", false);
|
||||
void testFeeExtension_unknownCurrency() {
|
||||
setEppInput(
|
||||
"domain_info_fee.xml",
|
||||
updateSubstitutions(
|
||||
SUBSTITUTION_BASE, "COMMAND", "create", "CURRENCY", "BAD", "PERIOD", "1"));
|
||||
EppException thrown = assertThrows(UnknownCurrencyEppException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,11 +120,13 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
"CURRENCY", "USD");
|
||||
|
||||
private static final ImmutableMap<String, String> FEE_06_MAP =
|
||||
updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "0.6", "FEE_NS", "fee");
|
||||
updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "fee-0.6", "FEE_NS", "fee");
|
||||
private static final ImmutableMap<String, String> FEE_11_MAP =
|
||||
updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "0.11", "FEE_NS", "fee11");
|
||||
updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "fee-0.11", "FEE_NS", "fee11");
|
||||
private static final ImmutableMap<String, String> FEE_12_MAP =
|
||||
updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "0.12", "FEE_NS", "fee12");
|
||||
updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "fee-0.12", "FEE_NS", "fee12");
|
||||
private static final ImmutableMap<String, String> FEE_STD_1_0_MAP =
|
||||
updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "epp:fee-1.0", "FEE_NS", "fee1_00");
|
||||
|
||||
private final DateTime expirationTime = DateTime.parse("2000-04-03T22:00:00.0Z");
|
||||
|
||||
@@ -390,7 +392,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_internalRegiration_premiumDomain() throws Exception {
|
||||
void testSuccess_internalRegiration_premiumDomain_std_v1() throws Exception {
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
@@ -398,7 +400,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
.build());
|
||||
persistDomain(SPECIFIED, Money.of(USD, 2));
|
||||
setRegistrarIdForFlow("NewRegistrar");
|
||||
ImmutableMap<String, String> customFeeMap = updateSubstitutions(FEE_06_MAP, "FEE", "10.00");
|
||||
ImmutableMap<String, String> customFeeMap =
|
||||
updateSubstitutions(FEE_STD_1_0_MAP, "FEE", "10.00");
|
||||
setEppInput("domain_renew_fee.xml", customFeeMap);
|
||||
doSuccessfulTest(
|
||||
"domain_renew_response_fee.xml",
|
||||
@@ -427,7 +430,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_anchorTenant_premiumDomain() throws Exception {
|
||||
void testSuccess_anchorTenant_premiumDomain_std_v1() throws Exception {
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
@@ -435,7 +438,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
.build());
|
||||
persistDomain(NONPREMIUM, null);
|
||||
setRegistrarIdForFlow("NewRegistrar");
|
||||
ImmutableMap<String, String> customFeeMap = updateSubstitutions(FEE_06_MAP, "FEE", "55.00");
|
||||
ImmutableMap<String, String> customFeeMap =
|
||||
updateSubstitutions(FEE_STD_1_0_MAP, "FEE", "55.00");
|
||||
setEppInput("domain_renew_fee.xml", customFeeMap);
|
||||
doSuccessfulTest(
|
||||
"domain_renew_response_fee.xml",
|
||||
@@ -449,12 +453,12 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_customLogicFee() throws Exception {
|
||||
void testSuccess_customLogicFee_std_v1() throws Exception {
|
||||
// The "costly-renew" domain has an additional RENEW fee of 100 from custom logic on top of the
|
||||
// normal $11 standard renew price for this TLD.
|
||||
ImmutableMap<String, String> customFeeMap =
|
||||
updateSubstitutions(
|
||||
FEE_06_MAP,
|
||||
FEE_STD_1_0_MAP,
|
||||
"NAME",
|
||||
"costly-renew.tld",
|
||||
"PERIOD",
|
||||
@@ -475,121 +479,45 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
|
||||
void testSuccess_fee_std_v1() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_STD_1_0_MAP);
|
||||
persistDomain();
|
||||
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_06_MAP);
|
||||
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_STD_1_0_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v11() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_11_MAP);
|
||||
void testSuccess_fee_withDefaultAttributes_std_v1() throws Exception {
|
||||
setEppInput("domain_renew_fee_defaults.xml", FEE_STD_1_0_MAP);
|
||||
persistDomain();
|
||||
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v12() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_12_MAP);
|
||||
persistDomain();
|
||||
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_12_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee_defaults.xml", FEE_06_MAP);
|
||||
persistDomain();
|
||||
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_06_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v11() throws Exception {
|
||||
setEppInput("domain_renew_fee_defaults.xml", FEE_11_MAP);
|
||||
persistDomain();
|
||||
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v12() throws Exception {
|
||||
setEppInput("domain_renew_fee_defaults.xml", FEE_12_MAP);
|
||||
persistDomain();
|
||||
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_12_MAP);
|
||||
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_STD_1_0_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_fee_unknownCurrency() {
|
||||
setEppInput("domain_renew_fee.xml", updateSubstitutions(FEE_06_MAP, "CURRENCY", "BAD"));
|
||||
setEppInput("domain_renew_fee.xml", updateSubstitutions(FEE_STD_1_0_MAP, "CURRENCY", "BAD"));
|
||||
EppException thrown = assertThrows(UnknownCurrencyEppException.class, this::persistDomain);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee_refundable.xml", FEE_06_MAP);
|
||||
void testFailure_refundableFee_std_v1() throws Exception {
|
||||
setEppInput("domain_renew_fee_refundable.xml", FEE_STD_1_0_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v11() throws Exception {
|
||||
setEppInput("domain_renew_fee_refundable.xml", FEE_11_MAP);
|
||||
void testFailure_gracePeriodFee_std_v1() throws Exception {
|
||||
setEppInput("domain_renew_fee_grace_period.xml", FEE_STD_1_0_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v12() throws Exception {
|
||||
setEppInput("domain_renew_fee_refundable.xml", FEE_12_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee_grace_period.xml", FEE_06_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v11() throws Exception {
|
||||
setEppInput("domain_renew_fee_grace_period.xml", FEE_11_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v12() throws Exception {
|
||||
setEppInput("domain_renew_fee_grace_period.xml", FEE_12_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee_applied.xml", FEE_06_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v11() throws Exception {
|
||||
setEppInput("domain_renew_fee_applied.xml", FEE_11_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v12() throws Exception {
|
||||
setEppInput("domain_renew_fee_applied.xml", FEE_12_MAP);
|
||||
void testFailure_appliedFee_std_v1() throws Exception {
|
||||
setEppInput("domain_renew_fee_applied.xml", FEE_STD_1_0_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
@@ -991,8 +919,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
|
||||
void testFailure_wrongFeeAmount_std_v1() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_STD_1_0_MAP);
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
@@ -1004,34 +932,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v11() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_11_MAP);
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
|
||||
.build());
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v12() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_12_MAP);
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
|
||||
.build());
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
|
||||
void testFailure_wrongCurrency_std_v1() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_STD_1_0_MAP);
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
@@ -1050,64 +952,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v11() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_11_MAP);
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setCurrency(EUR)
|
||||
.setCreateBillingCostTransitions(
|
||||
ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 13)))
|
||||
.setRestoreBillingCost(Money.of(EUR, 11))
|
||||
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 7)))
|
||||
.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(EUR)))
|
||||
.setRegistryLockOrUnlockBillingCost(Money.of(EUR, 20))
|
||||
.setServerStatusChangeBillingCost(Money.of(EUR, 19))
|
||||
.build());
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v12() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_12_MAP);
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setCurrency(EUR)
|
||||
.setCreateBillingCostTransitions(
|
||||
ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 13)))
|
||||
.setRestoreBillingCost(Money.of(EUR, 11))
|
||||
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 7)))
|
||||
.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(EUR)))
|
||||
.setRegistryLockOrUnlockBillingCost(Money.of(EUR, 20))
|
||||
.setServerStatusChangeBillingCost(Money.of(EUR, 19))
|
||||
.build());
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee_bad_scale.xml", FEE_06_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v11() throws Exception {
|
||||
setEppInput("domain_renew_fee_bad_scale.xml", FEE_11_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v12() throws Exception {
|
||||
setEppInput("domain_renew_fee_bad_scale.xml", FEE_12_MAP);
|
||||
void testFailure_feeGivenInWrongScale_std_v1() throws Exception {
|
||||
setEppInput("domain_renew_fee_bad_scale.xml", FEE_STD_1_0_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
@@ -1522,8 +1368,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_doesNotApplyNonPremiumDefaultTokenToPremiumName() throws Exception {
|
||||
ImmutableMap<String, String> customFeeMap = updateSubstitutions(FEE_06_MAP, "FEE", "500");
|
||||
void testSuccess_doesNotApplyNonPremiumDefaultTokenToPremiumName_std_v1() throws Exception {
|
||||
ImmutableMap<String, String> customFeeMap = updateSubstitutions(FEE_STD_1_0_MAP, "FEE", "500");
|
||||
setEppInput("domain_renew_fee.xml", customFeeMap);
|
||||
persistDomain();
|
||||
AllocationToken defaultToken1 =
|
||||
@@ -1556,9 +1402,9 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
"CURRENCY",
|
||||
"USD",
|
||||
"FEE_VERSION",
|
||||
"0.6",
|
||||
"epp:fee-1.0",
|
||||
"FEE_NS",
|
||||
"fee")));
|
||||
"fee1_00")));
|
||||
BillingEvent billingEvent =
|
||||
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.class));
|
||||
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
|
||||
@@ -1661,6 +1507,256 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 13.75));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_wrongFeeAmountTooHigh_defaultToken_std_v1() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_STD_1_0_MAP);
|
||||
persistDomain();
|
||||
AllocationToken defaultToken1 =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("aaaaa")
|
||||
.setTokenType(DEFAULT_PROMO)
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountYears(1)
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.build());
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setDefaultPromoTokens(ImmutableList.of(defaultToken1.createVKey()))
|
||||
.build());
|
||||
runFlowAssertResponse(
|
||||
loadFile(
|
||||
"domain_renew_response_fee.xml",
|
||||
ImmutableMap.of(
|
||||
"NAME",
|
||||
"example.tld",
|
||||
"PERIOD",
|
||||
"5",
|
||||
"EX_DATE",
|
||||
"2005-04-03T22:00:00.0Z",
|
||||
"FEE",
|
||||
"49.50",
|
||||
"CURRENCY",
|
||||
"USD",
|
||||
"FEE_VERSION",
|
||||
"epp:fee-1.0",
|
||||
"FEE_NS",
|
||||
"fee1_00")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmountTooLow_defaultToken_std_v1() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_STD_1_0_MAP);
|
||||
persistDomain();
|
||||
AllocationToken defaultToken1 =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("aaaaa")
|
||||
.setTokenType(DEFAULT_PROMO)
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountYears(1)
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.build());
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setDefaultPromoTokens(ImmutableList.of(defaultToken1.createVKey()))
|
||||
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
|
||||
.build());
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
|
||||
.build());
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v11() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_11_MAP);
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
|
||||
.build());
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v12() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_12_MAP);
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
|
||||
.build());
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setCurrency(EUR)
|
||||
.setCreateBillingCostTransitions(
|
||||
ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 13)))
|
||||
.setRestoreBillingCost(Money.of(EUR, 11))
|
||||
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 7)))
|
||||
.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(EUR)))
|
||||
.setRegistryLockOrUnlockBillingCost(Money.of(EUR, 20))
|
||||
.setServerStatusChangeBillingCost(Money.of(EUR, 19))
|
||||
.build());
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v11() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_11_MAP);
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setCurrency(EUR)
|
||||
.setCreateBillingCostTransitions(
|
||||
ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 13)))
|
||||
.setRestoreBillingCost(Money.of(EUR, 11))
|
||||
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 7)))
|
||||
.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(EUR)))
|
||||
.setRegistryLockOrUnlockBillingCost(Money.of(EUR, 20))
|
||||
.setServerStatusChangeBillingCost(Money.of(EUR, 19))
|
||||
.build());
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v12() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_12_MAP);
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setCurrency(EUR)
|
||||
.setCreateBillingCostTransitions(
|
||||
ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 13)))
|
||||
.setRestoreBillingCost(Money.of(EUR, 11))
|
||||
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 7)))
|
||||
.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(EUR)))
|
||||
.setRegistryLockOrUnlockBillingCost(Money.of(EUR, 20))
|
||||
.setServerStatusChangeBillingCost(Money.of(EUR, 19))
|
||||
.build());
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee_bad_scale.xml", FEE_06_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v11() throws Exception {
|
||||
setEppInput("domain_renew_fee_bad_scale.xml", FEE_11_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v12() throws Exception {
|
||||
setEppInput("domain_renew_fee_bad_scale.xml", FEE_12_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_doesNotApplyNonPremiumDefaultTokenToPremiumName_v06() throws Exception {
|
||||
ImmutableMap<String, String> customFeeMap = updateSubstitutions(FEE_06_MAP, "FEE", "500");
|
||||
setEppInput("domain_renew_fee.xml", customFeeMap);
|
||||
persistDomain();
|
||||
AllocationToken defaultToken1 =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("aaaaa")
|
||||
.setTokenType(DEFAULT_PROMO)
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountYears(1)
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.build());
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setDefaultPromoTokens(ImmutableList.of(defaultToken1.createVKey()))
|
||||
.setPremiumList(persistPremiumList("tld", USD, "example,USD 100"))
|
||||
.build());
|
||||
runFlowAssertResponse(
|
||||
loadFile(
|
||||
"domain_renew_response_fee.xml",
|
||||
ImmutableMap.of(
|
||||
"NAME",
|
||||
"example.tld",
|
||||
"PERIOD",
|
||||
"5",
|
||||
"EX_DATE",
|
||||
"2005-04-03T22:00:00.0Z",
|
||||
"FEE",
|
||||
"500.00",
|
||||
"CURRENCY",
|
||||
"USD",
|
||||
"FEE_VERSION",
|
||||
"fee-0.6",
|
||||
"FEE_NS",
|
||||
"fee")));
|
||||
BillingEvent billingEvent =
|
||||
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.class));
|
||||
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
|
||||
assertThat(billingEvent.getAllocationToken()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_internalRegiration_premiumDomain_v06() throws Exception {
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setPremiumList(persistPremiumList("tld", USD, "example,USD 100"))
|
||||
.build());
|
||||
persistDomain(SPECIFIED, Money.of(USD, 2));
|
||||
setRegistrarIdForFlow("NewRegistrar");
|
||||
ImmutableMap<String, String> customFeeMap = updateSubstitutions(FEE_06_MAP, "FEE", "10.00");
|
||||
setEppInput("domain_renew_fee.xml", customFeeMap);
|
||||
doSuccessfulTest(
|
||||
"domain_renew_response_fee.xml",
|
||||
5,
|
||||
"NewRegistrar",
|
||||
UserPrivileges.SUPERUSER,
|
||||
customFeeMap,
|
||||
Money.of(USD, 10),
|
||||
SPECIFIED,
|
||||
Money.of(USD, 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_wrongFeeAmountTooHigh_defaultToken_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
|
||||
@@ -1694,7 +1790,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
"CURRENCY",
|
||||
"USD",
|
||||
"FEE_VERSION",
|
||||
"0.6",
|
||||
"fee-0.6",
|
||||
"FEE_NS",
|
||||
"fee")));
|
||||
}
|
||||
@@ -1755,7 +1851,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
"CURRENCY",
|
||||
"USD",
|
||||
"FEE_VERSION",
|
||||
"0.11",
|
||||
"fee-0.11",
|
||||
"FEE_NS",
|
||||
"fee")));
|
||||
}
|
||||
@@ -1816,7 +1912,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
"CURRENCY",
|
||||
"USD",
|
||||
"FEE_VERSION",
|
||||
"0.12",
|
||||
"fee-0.12",
|
||||
"FEE_NS",
|
||||
"fee")));
|
||||
}
|
||||
@@ -1843,4 +1939,166 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee_refundable.xml", FEE_06_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v11() throws Exception {
|
||||
setEppInput("domain_renew_fee_refundable.xml", FEE_11_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v12() throws Exception {
|
||||
setEppInput("domain_renew_fee_refundable.xml", FEE_12_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee_grace_period.xml", FEE_06_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v11() throws Exception {
|
||||
setEppInput("domain_renew_fee_grace_period.xml", FEE_11_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v12() throws Exception {
|
||||
setEppInput("domain_renew_fee_grace_period.xml", FEE_12_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee_applied.xml", FEE_06_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v11() throws Exception {
|
||||
setEppInput("domain_renew_fee_applied.xml", FEE_11_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v12() throws Exception {
|
||||
setEppInput("domain_renew_fee_applied.xml", FEE_12_MAP);
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
|
||||
persistDomain();
|
||||
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_06_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v11() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_11_MAP);
|
||||
persistDomain();
|
||||
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v12() throws Exception {
|
||||
setEppInput("domain_renew_fee.xml", FEE_12_MAP);
|
||||
persistDomain();
|
||||
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_12_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v06() throws Exception {
|
||||
setEppInput("domain_renew_fee_defaults.xml", FEE_06_MAP);
|
||||
persistDomain();
|
||||
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_06_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v11() throws Exception {
|
||||
setEppInput("domain_renew_fee_defaults.xml", FEE_11_MAP);
|
||||
persistDomain();
|
||||
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v12() throws Exception {
|
||||
setEppInput("domain_renew_fee_defaults.xml", FEE_12_MAP);
|
||||
persistDomain();
|
||||
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_12_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_anchorTenant_premiumDomain_v06() throws Exception {
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setPremiumList(persistPremiumList("tld", USD, "example,USD 100"))
|
||||
.build());
|
||||
persistDomain(NONPREMIUM, null);
|
||||
setRegistrarIdForFlow("NewRegistrar");
|
||||
ImmutableMap<String, String> customFeeMap = updateSubstitutions(FEE_06_MAP, "FEE", "55.00");
|
||||
setEppInput("domain_renew_fee.xml", customFeeMap);
|
||||
doSuccessfulTest(
|
||||
"domain_renew_response_fee.xml",
|
||||
5,
|
||||
"NewRegistrar",
|
||||
UserPrivileges.SUPERUSER,
|
||||
customFeeMap,
|
||||
Money.of(USD, 55),
|
||||
NONPREMIUM,
|
||||
null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_customLogicFee_v06() throws Exception {
|
||||
// The "costly-renew" domain has an additional RENEW fee of 100 from custom logic on top of the
|
||||
// normal $11 standard renew price for this TLD.
|
||||
ImmutableMap<String, String> customFeeMap =
|
||||
updateSubstitutions(
|
||||
FEE_06_MAP,
|
||||
"NAME",
|
||||
"costly-renew.tld",
|
||||
"PERIOD",
|
||||
"1",
|
||||
"EX_DATE",
|
||||
"2001-04-03T22:00:00.0Z",
|
||||
"FEE",
|
||||
"111.00");
|
||||
setEppInput("domain_renew_fee.xml", customFeeMap);
|
||||
persistDomain();
|
||||
doSuccessfulTest(
|
||||
"domain_renew_response_fee.xml",
|
||||
1,
|
||||
"TheRegistrar",
|
||||
UserPrivileges.NORMAL,
|
||||
customFeeMap,
|
||||
Money.of(USD, 111));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,11 +87,13 @@ import org.junit.jupiter.api.Test;
|
||||
class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreRequestFlow, Domain> {
|
||||
|
||||
private static final ImmutableMap<String, String> FEE_06_MAP =
|
||||
ImmutableMap.of("FEE_VERSION", "0.6", "FEE_NS", "fee", "CURRENCY", "USD");
|
||||
ImmutableMap.of("FEE_VERSION", "fee-0.6", "FEE_NS", "fee", "CURRENCY", "USD");
|
||||
private static final ImmutableMap<String, String> FEE_11_MAP =
|
||||
ImmutableMap.of("FEE_VERSION", "0.11", "FEE_NS", "fee11", "CURRENCY", "USD");
|
||||
ImmutableMap.of("FEE_VERSION", "fee-0.11", "FEE_NS", "fee11", "CURRENCY", "USD");
|
||||
private static final ImmutableMap<String, String> FEE_12_MAP =
|
||||
ImmutableMap.of("FEE_VERSION", "0.12", "FEE_NS", "fee12", "CURRENCY", "USD");
|
||||
ImmutableMap.of("FEE_VERSION", "fee-0.12", "FEE_NS", "fee12", "CURRENCY", "USD");
|
||||
private static final ImmutableMap<String, String> FEE_STD_1_0_MAP =
|
||||
ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "FEE_NS", "fee1_00", "CURRENCY", "USD");
|
||||
|
||||
@BeforeEach
|
||||
void initDomainTest() {
|
||||
@@ -308,7 +310,7 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
|
||||
|
||||
@Test
|
||||
void testSuccess_autorenewEndTimeIsCleared() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee.xml", FEE_06_MAP);
|
||||
setEppInput("domain_update_restore_request_fee.xml", FEE_STD_1_0_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
persistResource(
|
||||
reloadResourceByForeignKey()
|
||||
@@ -316,64 +318,31 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
|
||||
.setAutorenewEndTime(Optional.of(clock.nowUtc().plusYears(2)))
|
||||
.build());
|
||||
assertThat(reloadResourceByForeignKey().getAutorenewEndTime()).isPresent();
|
||||
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_06_MAP));
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_update_restore_request_response_fee.xml", FEE_STD_1_0_MAP));
|
||||
assertThat(reloadResourceByForeignKey().getAutorenewEndTime()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v06() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee.xml", FEE_06_MAP);
|
||||
void testSuccess_fee_std_v1() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee.xml", FEE_STD_1_0_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_06_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v06_noRenewal() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_no_renewal.xml", FEE_06_MAP);
|
||||
persistPendingDeleteDomain(clock.nowUtc().plusMonths(6));
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_update_restore_request_response_fee_no_renewal.xml", FEE_06_MAP));
|
||||
loadFile("domain_update_restore_request_response_fee.xml", FEE_STD_1_0_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v11() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee.xml", FEE_11_MAP);
|
||||
void testSuccess_fee_withDefaultAttributes_std_v1() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_defaults.xml", FEE_STD_1_0_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_11_MAP));
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_update_restore_request_response_fee.xml", FEE_STD_1_0_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v12() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee.xml", FEE_12_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_12_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v06() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_defaults.xml", FEE_06_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_06_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v11() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_defaults.xml", FEE_11_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_11_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v12() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_defaults.xml", FEE_12_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_12_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_fee_unknownCurrency() {
|
||||
void testFailure_fee_unknownCurrency_std_v1() {
|
||||
ImmutableMap<String, String> substitutions =
|
||||
ImmutableMap.of("FEE_VERSION", "0.12", "FEE_NS", "fee12", "CURRENCY", "BAD");
|
||||
ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "FEE_NS", "fee1_00", "CURRENCY", "BAD");
|
||||
setEppInput("domain_update_restore_request_fee.xml", substitutions);
|
||||
EppException thrown =
|
||||
assertThrows(UnknownCurrencyEppException.class, this::persistPendingDeleteDomain);
|
||||
@@ -381,72 +350,24 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v06() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_refundable.xml", FEE_06_MAP);
|
||||
void testFailure_refundableFee_std_v1() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_refundable.xml", FEE_STD_1_0_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v11() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_refundable.xml", FEE_11_MAP);
|
||||
void testFailure_gracePeriodFee_std_v1() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_grace_period.xml", FEE_STD_1_0_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v12() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_refundable.xml", FEE_12_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v06() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_grace_period.xml", FEE_06_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v11() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_grace_period.xml", FEE_11_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v12() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_grace_period.xml", FEE_12_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v06() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_applied.xml", FEE_06_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v11() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_applied.xml", FEE_11_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v12() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_applied.xml", FEE_12_MAP);
|
||||
void testFailure_appliedFee_std_v1() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_applied.xml", FEE_STD_1_0_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
@@ -455,18 +376,19 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
|
||||
@Test
|
||||
void testSuccess_premiumNotBlocked() throws Exception {
|
||||
createTld("example");
|
||||
setEppInput("domain_update_restore_request_premium.xml");
|
||||
setEppInput("domain_update_restore_request_premium.xml", FEE_STD_1_0_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
runFlowAssertResponse(loadFile("domain_update_restore_request_response_premium.xml"));
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_update_restore_request_response_premium.xml", FEE_STD_1_0_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_premiumNotBlocked_andNoRenewal() throws Exception {
|
||||
void testSuccess_premiumNotBlocked_andNoRenewal_std_v1() throws Exception {
|
||||
createTld("example");
|
||||
setEppInput("domain_update_restore_request_premium_no_renewal.xml");
|
||||
setEppInput("domain_update_restore_request_premium_no_renewal.xml", FEE_STD_1_0_MAP);
|
||||
persistPendingDeleteDomain(clock.nowUtc().plusYears(2));
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_update_restore_request_response_fee_no_renewal.xml", FEE_12_MAP));
|
||||
loadFile("domain_update_restore_request_response_fee_no_renewal.xml", FEE_STD_1_0_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -484,14 +406,14 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
|
||||
@Test
|
||||
void testSuccess_superuserOverridesPremiumNameBlock() throws Exception {
|
||||
createTld("example");
|
||||
setEppInput("domain_update_restore_request_premium.xml");
|
||||
setEppInput("domain_update_restore_request_premium.xml", FEE_STD_1_0_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
// Modify the Registrar to block premium names.
|
||||
persistResource(loadRegistrar("TheRegistrar").asBuilder().setBlockPremiumNames(true).build());
|
||||
runFlowAssertResponse(
|
||||
CommitMode.LIVE,
|
||||
UserPrivileges.SUPERUSER,
|
||||
loadFile("domain_update_restore_request_response_premium.xml"));
|
||||
loadFile("domain_update_restore_request_response_premium.xml", FEE_STD_1_0_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -538,26 +460,8 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v06() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee.xml", FEE_06_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
persistResource(Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, 100)).build());
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v11() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee.xml", FEE_11_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
persistResource(Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, 100)).build());
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v12() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee.xml", FEE_12_MAP);
|
||||
void testFailure_wrongFeeAmount_std_v1() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee.xml", FEE_STD_1_0_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
persistResource(Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, 100)).build());
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
@@ -584,39 +488,13 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v06() throws Exception {
|
||||
runWrongCurrencyTest(FEE_06_MAP);
|
||||
void testFailure_wrongCurrency_std_v1() throws Exception {
|
||||
runWrongCurrencyTest(FEE_STD_1_0_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v11() throws Exception {
|
||||
runWrongCurrencyTest(FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v12() throws Exception {
|
||||
runWrongCurrencyTest(FEE_12_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v06() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_bad_scale.xml", FEE_06_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v11() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_bad_scale.xml", FEE_11_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v12() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_bad_scale.xml", FEE_12_MAP);
|
||||
void testFailure_feeGivenInWrongScale_std_v1() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_bad_scale.xml", FEE_STD_1_0_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
@@ -738,9 +616,9 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_premiumBlocked() throws Exception {
|
||||
void testFailure_premiumBlocked_std_v1() throws Exception {
|
||||
createTld("example");
|
||||
setEppInput("domain_update_restore_request_premium.xml");
|
||||
setEppInput("domain_update_restore_request_premium.xml", FEE_STD_1_0_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
// Modify the Registrar to block premium names.
|
||||
persistResource(loadRegistrar("TheRegistrar").asBuilder().setBlockPremiumNames(true).build());
|
||||
@@ -805,4 +683,222 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
|
||||
assertThat(thrown).hasMessageThat().contains("domain restore reports are not supported");
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v06() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee.xml", FEE_06_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
persistResource(Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, 100)).build());
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v11() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee.xml", FEE_11_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
persistResource(Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, 100)).build());
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v12() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee.xml", FEE_12_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
persistResource(Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, 100)).build());
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v06() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_bad_scale.xml", FEE_06_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v11() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_bad_scale.xml", FEE_11_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v12() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_bad_scale.xml", FEE_12_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v06() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_applied.xml", FEE_06_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v11() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_applied.xml", FEE_11_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v12() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_applied.xml", FEE_12_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v06() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_defaults.xml", FEE_06_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_06_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v11() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_defaults.xml", FEE_11_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_11_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v12() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_defaults.xml", FEE_12_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_12_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v06() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee.xml", FEE_06_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_06_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v06_noRenewal() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_no_renewal.xml", FEE_06_MAP);
|
||||
persistPendingDeleteDomain(clock.nowUtc().plusMonths(6));
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_update_restore_request_response_fee_no_renewal.xml", FEE_06_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v11() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee.xml", FEE_11_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_11_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v12() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee.xml", FEE_12_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_12_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v06() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_refundable.xml", FEE_06_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v11() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_refundable.xml", FEE_11_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v12() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_refundable.xml", FEE_12_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v06() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_grace_period.xml", FEE_06_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v11() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_grace_period.xml", FEE_11_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v12() throws Exception {
|
||||
setEppInput("domain_update_restore_request_fee_grace_period.xml", FEE_12_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v06() throws Exception {
|
||||
runWrongCurrencyTest(FEE_06_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v11() throws Exception {
|
||||
runWrongCurrencyTest(FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v12() throws Exception {
|
||||
runWrongCurrencyTest(FEE_12_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_premiumBlocked_v12() throws Exception {
|
||||
createTld("example");
|
||||
setEppInput("domain_update_restore_request_premium.xml", FEE_12_MAP);
|
||||
persistPendingDeleteDomain();
|
||||
// Modify the Registrar to block premium names.
|
||||
persistResource(loadRegistrar("TheRegistrar").asBuilder().setBlockPremiumNames(true).build());
|
||||
EppException thrown = assertThrows(PremiumNameBlockedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_premiumNotBlocked_andNoRenewal_v12() throws Exception {
|
||||
createTld("example");
|
||||
setEppInput("domain_update_restore_request_premium_no_renewal.xml", FEE_12_MAP);
|
||||
persistPendingDeleteDomain(clock.nowUtc().plusYears(2));
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_update_restore_request_response_fee_no_renewal.xml", FEE_12_MAP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_fee_unknownCurrency_v12() {
|
||||
ImmutableMap<String, String> substitutions =
|
||||
ImmutableMap.of("FEE_VERSION", "fee-0.12", "FEE_NS", "fee12", "CURRENCY", "BAD");
|
||||
setEppInput("domain_update_restore_request_fee.xml", substitutions);
|
||||
EppException thrown =
|
||||
assertThrows(UnknownCurrencyEppException.class, this::persistPendingDeleteDomain);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,6 @@ import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
|
||||
import google.registry.model.billing.BillingCancellation;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingRecurrence;
|
||||
import google.registry.model.contact.ContactAuthInfo;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainAuthInfo;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
@@ -153,12 +152,6 @@ class DomainTransferApproveFlowTest
|
||||
.build());
|
||||
}
|
||||
|
||||
private void setEppLoader(String commandFilename) {
|
||||
setEppInput(commandFilename);
|
||||
// Replace the ROID in the xml file with the one generated in our test.
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a successful test, with the expectedCancellationBillingEvents parameter containing a list
|
||||
* of billing event builders that will be filled out with the correct HistoryEntry parent as it is
|
||||
@@ -184,7 +177,7 @@ class DomainTransferApproveFlowTest
|
||||
String expectedXmlFilename,
|
||||
DateTime expectedExpirationTime)
|
||||
throws Exception {
|
||||
setEppLoader(commandFilename);
|
||||
setEppInput(commandFilename);
|
||||
Tld registry = Tld.get(tld);
|
||||
domain = reloadResourceByForeignKey();
|
||||
// Make sure the implicit billing event is there; it will be deleted by the flow.
|
||||
@@ -361,7 +354,7 @@ class DomainTransferApproveFlowTest
|
||||
}
|
||||
|
||||
private void doFailingTest(String commandFilename) throws Exception {
|
||||
setEppLoader(commandFilename);
|
||||
setEppInput(commandFilename);
|
||||
// Setup done; run the test.
|
||||
assertMutatingFlow(true);
|
||||
runFlow();
|
||||
@@ -376,7 +369,7 @@ class DomainTransferApproveFlowTest
|
||||
|
||||
@Test
|
||||
void testDryRun() throws Exception {
|
||||
setEppLoader("domain_transfer_approve.xml");
|
||||
setEppInput("domain_transfer_approve.xml");
|
||||
dryRunFlowAssertResponse(loadFile("domain_transfer_approve_response.xml"));
|
||||
}
|
||||
|
||||
@@ -492,14 +485,6 @@ class DomainTransferApproveFlowTest
|
||||
"domain_transfer_approve_response.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_contactAuthInfo() throws Exception {
|
||||
doSuccessfulTest(
|
||||
"tld",
|
||||
"domain_transfer_approve_contact_authinfo.xml",
|
||||
"domain_transfer_approve_response.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_autorenewBeforeTransfer() throws Exception {
|
||||
domain = reloadResourceByForeignKey();
|
||||
@@ -619,14 +604,8 @@ class DomainTransferApproveFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_badContactPassword() {
|
||||
// Change the contact's password so it does not match the password in the file.
|
||||
contact =
|
||||
persistResource(
|
||||
contact
|
||||
.asBuilder()
|
||||
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
|
||||
.build());
|
||||
void testFailure_contactPassword() {
|
||||
// Contact passwords cannot be provided because we don't store contacts
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
BadAuthInfoForResourceException.class,
|
||||
|
||||
@@ -43,7 +43,6 @@ import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
|
||||
import google.registry.flows.exceptions.NotPendingTransferException;
|
||||
import google.registry.flows.exceptions.NotTransferInitiatorException;
|
||||
import google.registry.model.contact.ContactAuthInfo;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainAuthInfo;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
@@ -75,8 +74,6 @@ class DomainTransferCancelFlowTest
|
||||
private void doSuccessfulTest(String commandFilename) throws Exception {
|
||||
setEppInput(commandFilename);
|
||||
|
||||
// Replace the ROID in the xml file with the one generated in our test.
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
// Make sure the implicit billing event is there; it will be deleted by the flow.
|
||||
// We also expect to see autorenew events for the gaining and losing registrars.
|
||||
assertBillingEvents(
|
||||
@@ -187,8 +184,6 @@ class DomainTransferCancelFlowTest
|
||||
|
||||
private void doFailingTest(String commandFilename) throws Exception {
|
||||
setEppInput(commandFilename);
|
||||
// Replace the ROID in the xml file with the one generated in our test.
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
// Setup done; run the test.
|
||||
assertMutatingFlow(true);
|
||||
runFlow();
|
||||
@@ -204,7 +199,6 @@ class DomainTransferCancelFlowTest
|
||||
@Test
|
||||
void testDryRun() throws Exception {
|
||||
setEppInput("domain_transfer_cancel.xml");
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
dryRunFlowAssertResponse(loadFile("domain_transfer_cancel_response.xml"));
|
||||
}
|
||||
|
||||
@@ -219,19 +213,8 @@ class DomainTransferCancelFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_contactAuthInfo() throws Exception {
|
||||
doSuccessfulTest("domain_transfer_cancel_contact_authinfo.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_badContactPassword() {
|
||||
// Change the contact's password so it does not match the password in the file.
|
||||
contact =
|
||||
persistResource(
|
||||
contact
|
||||
.asBuilder()
|
||||
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
|
||||
.build());
|
||||
void testFailure_contactPassword() {
|
||||
// Contact passwords cannot be provided because we don't store contacts
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
BadAuthInfoForResourceException.class,
|
||||
|
||||
@@ -20,7 +20,6 @@ import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
|
||||
import static google.registry.testing.DatabaseHelper.createBillingEventForTransfer;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistDomainWithDependentResources;
|
||||
import static google.registry.testing.DatabaseHelper.persistDomainWithPendingTransfer;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
@@ -36,7 +35,6 @@ import google.registry.model.billing.BillingBase.Flag;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingRecurrence;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
@@ -72,7 +70,6 @@ abstract class DomainTransferFlowTestCase<F extends Flow, R extends EppResource>
|
||||
static final DateTime EXTENDED_REGISTRATION_EXPIRATION_TIME =
|
||||
REGISTRATION_EXPIRATION_TIME.plusYears(EXTENDED_REGISTRATION_YEARS);
|
||||
|
||||
protected Contact contact;
|
||||
protected Domain domain;
|
||||
Host subordinateHost;
|
||||
private DomainHistory historyEntryDomainCreate;
|
||||
@@ -104,12 +101,10 @@ abstract class DomainTransferFlowTestCase<F extends Flow, R extends EppResource>
|
||||
/** Adds a domain with no pending transfer on it. */
|
||||
void setupDomain(String label, String tld) {
|
||||
createTld(tld);
|
||||
contact = persistActiveContact("jd1234");
|
||||
domain =
|
||||
persistDomainWithDependentResources(
|
||||
label,
|
||||
tld,
|
||||
contact,
|
||||
clock.nowUtc(),
|
||||
DateTime.parse("1999-04-03T22:00:00.0Z"),
|
||||
REGISTRATION_EXPIRATION_TIME);
|
||||
|
||||
@@ -29,7 +29,6 @@ import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
|
||||
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
|
||||
import google.registry.flows.exceptions.NoTransferHistoryToQueryException;
|
||||
import google.registry.flows.exceptions.NotAuthorizedToViewTransferException;
|
||||
import google.registry.model.contact.ContactAuthInfo;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainAuthInfo;
|
||||
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
|
||||
@@ -52,8 +51,6 @@ class DomainTransferQueryFlowTest
|
||||
private void doSuccessfulTest(
|
||||
String commandFilename, String expectedXmlFilename, int numPollMessages) throws Exception {
|
||||
setEppInput(commandFilename);
|
||||
// Replace the ROID in the xml file with the one generated in our test.
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
// Setup done; run the test.
|
||||
assertMutatingFlow(false);
|
||||
runFlowAssertResponse(loadFile(expectedXmlFilename));
|
||||
@@ -73,8 +70,6 @@ class DomainTransferQueryFlowTest
|
||||
|
||||
private void doFailingTest(String commandFilename) throws Exception {
|
||||
setEppInput(commandFilename);
|
||||
// Replace the ROID in the xml file with the one generated in our test.
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
// Setup done; run the test.
|
||||
assertMutatingFlow(false);
|
||||
runFlow();
|
||||
@@ -105,13 +100,6 @@ class DomainTransferQueryFlowTest
|
||||
"domain_transfer_query_domain_authinfo.xml", "domain_transfer_query_response.xml", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_contactAuthInfo() throws Exception {
|
||||
setRegistrarIdForFlow("ClientZ");
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_query_contact_authinfo.xml", "domain_transfer_query_response.xml", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_clientApproved() throws Exception {
|
||||
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
|
||||
@@ -170,14 +158,7 @@ class DomainTransferQueryFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_badContactPassword() {
|
||||
// Change the contact's password so it does not match the password in the file.
|
||||
contact =
|
||||
persistResource(
|
||||
contact
|
||||
.asBuilder()
|
||||
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
|
||||
.build());
|
||||
void testFailure_contactPasswordNotAllowed() {
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
BadAuthInfoForResourceException.class,
|
||||
|
||||
@@ -43,7 +43,6 @@ import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
|
||||
import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
|
||||
import google.registry.flows.exceptions.NotPendingTransferException;
|
||||
import google.registry.model.contact.ContactAuthInfo;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainAuthInfo;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
@@ -78,7 +77,6 @@ class DomainTransferRejectFlowTest
|
||||
private void doSuccessfulTest(String commandFilename, String expectedXmlFilename)
|
||||
throws Exception {
|
||||
setEppInput(commandFilename);
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
// Make sure the implicit billing event is there; it will be deleted by the flow.
|
||||
// We also expect to see autorenew events for the gaining and losing registrars.
|
||||
assertBillingEvents(
|
||||
@@ -149,8 +147,6 @@ class DomainTransferRejectFlowTest
|
||||
|
||||
private void doFailingTest(String commandFilename) throws Exception {
|
||||
setEppInput(commandFilename);
|
||||
// Replace the ROID in the xml file with the one generated in our test.
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
// Setup done; run the test.
|
||||
assertMutatingFlow(true);
|
||||
runFlow();
|
||||
@@ -171,7 +167,6 @@ class DomainTransferRejectFlowTest
|
||||
@Test
|
||||
void testDryRun() throws Exception {
|
||||
setEppInput("domain_transfer_reject.xml");
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
dryRunFlowAssertResponse(loadFile("domain_transfer_reject_response.xml"));
|
||||
}
|
||||
|
||||
@@ -181,12 +176,6 @@ class DomainTransferRejectFlowTest
|
||||
"domain_transfer_reject_domain_authinfo.xml", "domain_transfer_reject_response.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_contactAuthInfo() throws Exception {
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_reject_contact_authinfo.xml", "domain_transfer_reject_response.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_notAuthorizedForTld() {
|
||||
persistResource(
|
||||
@@ -209,14 +198,8 @@ class DomainTransferRejectFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_badContactPassword() {
|
||||
// Change the contact's password so it does not match the password in the file.
|
||||
contact =
|
||||
persistResource(
|
||||
contact
|
||||
.asBuilder()
|
||||
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
|
||||
.build());
|
||||
void testFailure_contactPassword() {
|
||||
// Contact passwords cannot be provided because we don't store contacts
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
BadAuthInfoForResourceException.class,
|
||||
|
||||
@@ -40,12 +40,12 @@ import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.loadByKey;
|
||||
import static google.registry.testing.DatabaseHelper.loadByKeys;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DomainSubject.assertAboutDomains;
|
||||
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
|
||||
import static google.registry.testing.HistoryEntrySubject.assertAboutHistoryEntries;
|
||||
import static google.registry.testing.HostSubject.assertAboutHosts;
|
||||
import static google.registry.testing.TestDataHelper.updateSubstitutions;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.JPY;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
@@ -95,7 +95,6 @@ import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
|
||||
import google.registry.model.billing.BillingCancellation;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingRecurrence;
|
||||
import google.registry.model.contact.ContactAuthInfo;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainAuthInfo;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
@@ -149,29 +148,31 @@ class DomainTransferRequestFlowTest
|
||||
private static final ImmutableMap<String, String> FEE_06_MAP =
|
||||
new ImmutableMap.Builder<String, String>()
|
||||
.putAll(BASE_FEE_MAP)
|
||||
.put("FEE_VERSION", "0.6")
|
||||
.put("FEE_VERSION", "fee-0.6")
|
||||
.put("FEE_NS", "fee")
|
||||
.build();
|
||||
private static final ImmutableMap<String, String> FEE_11_MAP =
|
||||
new ImmutableMap.Builder<String, String>()
|
||||
.putAll(BASE_FEE_MAP)
|
||||
.put("FEE_VERSION", "0.11")
|
||||
.put("FEE_VERSION", "fee-0.11")
|
||||
.put("FEE_NS", "fee11")
|
||||
.build();
|
||||
private static final ImmutableMap<String, String> FEE_12_MAP =
|
||||
new ImmutableMap.Builder<String, String>()
|
||||
.putAll(BASE_FEE_MAP)
|
||||
.put("FEE_VERSION", "0.12")
|
||||
.put("FEE_VERSION", "fee-0.12")
|
||||
.put("FEE_NS", "fee12")
|
||||
.build();
|
||||
private static final ImmutableMap<String, String> FEE_STD_1_0_MAP =
|
||||
updateSubstitutions(BASE_FEE_MAP, "FEE_VERSION", "epp:fee-1.0", "FEE_NS", "fee1_00");
|
||||
private static final ImmutableMap<String, String> RICH_DOMAIN_MAP =
|
||||
ImmutableMap.<String, String>builder()
|
||||
.put("DOMAIN", "rich.example")
|
||||
.put("YEARS", "1")
|
||||
.put("AMOUNT", "100.00")
|
||||
.put("CURRENCY", "USD")
|
||||
.put("FEE_VERSION", "0.12")
|
||||
.put("FEE_NS", "fee12")
|
||||
.put("FEE_VERSION", "epp:fee-1.0")
|
||||
.put("FEE_NS", "fee")
|
||||
.build();
|
||||
|
||||
@BeforeEach
|
||||
@@ -467,8 +468,6 @@ class DomainTransferRequestFlowTest
|
||||
throws Exception {
|
||||
setEppInput(commandFilename, substitutions);
|
||||
ImmutableSet<GracePeriod> originalGracePeriods = domain.getGracePeriods();
|
||||
// Replace the ROID in the xml file with the one generated in our test.
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
// For all of the other transfer flow tests, 'now' corresponds to day 3 of the transfer, but
|
||||
// for the request test we want that same 'now' to be the initial request time, so we shift
|
||||
// the transfer timeline 3 days later by adjusting the implicit transfer time here.
|
||||
@@ -568,8 +567,6 @@ class DomainTransferRequestFlowTest
|
||||
eppRequestSource = EppRequestSource.TOOL;
|
||||
setEppInput(commandFilename, substitutions);
|
||||
ImmutableSet<GracePeriod> originalGracePeriods = domain.getGracePeriods();
|
||||
// Replace the ROID in the xml file with the one generated in our test.
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
// For all of the other transfer flow tests, 'now' corresponds to day 3 of the transfer, but
|
||||
// for the request test we want that same 'now' to be the initial request time, so we shift
|
||||
// the transfer timeline 3 days later by adjusting the implicit transfer time here.
|
||||
@@ -623,8 +620,6 @@ class DomainTransferRequestFlowTest
|
||||
String commandFilename, UserPrivileges userPrivileges, Map<String, String> substitutions)
|
||||
throws Exception {
|
||||
setEppInput(commandFilename, substitutions);
|
||||
// Replace the ROID in the xml file with the one generated in our test.
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
// Setup done; run the test.
|
||||
assertMutatingFlow(true);
|
||||
runFlow(CommitMode.LIVE, userPrivileges);
|
||||
@@ -654,7 +649,6 @@ class DomainTransferRequestFlowTest
|
||||
void testDryRun() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
setEppInput("domain_transfer_request.xml");
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
dryRunFlowAssertResponse(loadFile("domain_transfer_request_response.xml"));
|
||||
}
|
||||
|
||||
@@ -665,140 +659,50 @@ class DomainTransferRequestFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v06() throws Exception {
|
||||
void testSuccess_fee_std_v1() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_request_fee.xml", "domain_transfer_request_response_fee.xml", FEE_06_MAP);
|
||||
"domain_transfer_request_fee.xml",
|
||||
"domain_transfer_request_response_fee.xml",
|
||||
FEE_STD_1_0_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v11() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_request_fee.xml", "domain_transfer_request_response_fee.xml", FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v12() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_request_fee.xml", "domain_transfer_request_response_fee.xml", FEE_12_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v06() throws Exception {
|
||||
void testSuccess_fee_withDefaultAttributes_std_v1() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_request_fee_defaults.xml",
|
||||
"domain_transfer_request_response_fee.xml",
|
||||
FEE_06_MAP);
|
||||
FEE_STD_1_0_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v11() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_request_fee_defaults.xml",
|
||||
"domain_transfer_request_response_fee.xml",
|
||||
FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v12() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_request_fee_defaults.xml",
|
||||
"domain_transfer_request_response_fee.xml",
|
||||
FEE_12_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v06() {
|
||||
void testFailure_refundableFee_std_v1() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_refundable.xml", FEE_06_MAP));
|
||||
() -> doFailingTest("domain_transfer_request_fee_refundable.xml", FEE_STD_1_0_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v11() {
|
||||
void testFailure_gracePeriodFee_std_v1() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_refundable.xml", FEE_11_MAP));
|
||||
() -> doFailingTest("domain_transfer_request_fee_grace_period.xml", FEE_STD_1_0_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v12() {
|
||||
void testFailure_appliedFee_std_v1() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_refundable.xml", FEE_12_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v06() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_grace_period.xml", FEE_06_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v11() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_grace_period.xml", FEE_11_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v12() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_grace_period.xml", FEE_12_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v06() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_applied.xml", FEE_06_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v11() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_applied.xml", FEE_11_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v12() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_applied.xml", FEE_12_MAP));
|
||||
() -> doFailingTest("domain_transfer_request_fee_applied.xml", FEE_STD_1_0_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@@ -991,7 +895,7 @@ class DomainTransferRequestFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_superuserExtension_zeroPeriod_feeTransferExtension() {
|
||||
void testFailure_superuserExtension_zeroPeriod_feeTransferExtension_std_v1() {
|
||||
setupDomain("example", "tld");
|
||||
eppRequestSource = EppRequestSource.TOOL;
|
||||
clock.advanceOneMilli();
|
||||
@@ -1001,7 +905,11 @@ class DomainTransferRequestFlowTest
|
||||
runTest(
|
||||
"domain_transfer_request_fee_and_superuser_extension.xml",
|
||||
UserPrivileges.SUPERUSER,
|
||||
ImmutableMap.of("PERIOD", "0", "AUTOMATIC_TRANSFER_LENGTH", "5")));
|
||||
new ImmutableMap.Builder<String, String>()
|
||||
.putAll(FEE_STD_1_0_MAP)
|
||||
.put("PERIOD", "0")
|
||||
.put("AUTOMATIC_TRANSFER_LENGTH", "5")
|
||||
.build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1063,7 +971,7 @@ class DomainTransferRequestFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_customLogicFee() throws Exception {
|
||||
void testSuccess_customLogicFee_std_v1() throws Exception {
|
||||
setupDomain("expensive-domain", "foo");
|
||||
clock.advanceOneMilli();
|
||||
doSuccessfulTest(
|
||||
@@ -1075,7 +983,7 @@ class DomainTransferRequestFlowTest
|
||||
.put("YEARS", "1")
|
||||
.put("AMOUNT", "111.00")
|
||||
.put("EXDATE", "2002-09-08T22:00:00.0Z")
|
||||
.put("FEE_VERSION", "0.6")
|
||||
.put("FEE_VERSION", "epp:fee-1.0")
|
||||
.put("FEE_NS", "fee")
|
||||
.build(),
|
||||
Optional.of(Money.of(USD, 111)));
|
||||
@@ -1227,7 +1135,7 @@ class DomainTransferRequestFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_premiumNotBlocked() throws Exception {
|
||||
void testSuccess_premiumNotBlocked_v06() throws Exception {
|
||||
setupDomain("rich", "example");
|
||||
clock.advanceOneMilli();
|
||||
// We don't verify the results; just check that the flow doesn't fail.
|
||||
@@ -1235,7 +1143,7 @@ class DomainTransferRequestFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_premiumNotBlockedInSuperuserMode() throws Exception {
|
||||
void testSuccess_premiumNotBlockedInSuperuserMode_std_v1() throws Exception {
|
||||
setupDomain("rich", "example");
|
||||
clock.advanceOneMilli();
|
||||
// Modify the Registrar to block premium names.
|
||||
@@ -1267,7 +1175,6 @@ class DomainTransferRequestFlowTest
|
||||
// This ensures that the transfer has non-premium cost, as otherwise, the fee extension would be
|
||||
// required to ack the premium price.
|
||||
setEppInput("domain_transfer_request.xml");
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
runFlowAssertResponse(loadFile("domain_transfer_request_response.xml"));
|
||||
domain = loadByEntity(domain);
|
||||
|
||||
@@ -1321,7 +1228,6 @@ class DomainTransferRequestFlowTest
|
||||
DateTime now = clock.nowUtc();
|
||||
|
||||
setEppInput("domain_transfer_request.xml");
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
runFlowAssertResponse(loadFile("domain_transfer_request_response.xml"));
|
||||
domain = loadByEntity(domain);
|
||||
|
||||
@@ -1383,7 +1289,6 @@ class DomainTransferRequestFlowTest
|
||||
DateTime now = clock.nowUtc();
|
||||
|
||||
setEppInput("domain_transfer_request.xml");
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
runFlowAssertResponse(loadFile("domain_transfer_request_response.xml"));
|
||||
domain = loadByEntity(domain);
|
||||
|
||||
@@ -1444,7 +1349,6 @@ class DomainTransferRequestFlowTest
|
||||
DateTime now = clock.nowUtc();
|
||||
|
||||
setEppInput("domain_transfer_request.xml");
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
runFlowAssertResponse(loadFile("domain_transfer_request_response.xml"));
|
||||
domain = loadByEntity(domain);
|
||||
|
||||
@@ -1525,24 +1429,14 @@ class DomainTransferRequestFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v06() {
|
||||
runWrongCurrencyTest(FEE_06_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v11() {
|
||||
runWrongCurrencyTest(FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v12() {
|
||||
runWrongCurrencyTest(FEE_12_MAP);
|
||||
void testFailure_wrongCurrency_std_v1() {
|
||||
runWrongCurrencyTest(FEE_STD_1_0_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_unknownCurrency() {
|
||||
Map<String, String> substitutions = Maps.newHashMap();
|
||||
substitutions.putAll(FEE_06_MAP);
|
||||
substitutions.putAll(FEE_STD_1_0_MAP);
|
||||
substitutions.put("CURRENCY", "BAD");
|
||||
setupDomain("example", "tld");
|
||||
setEppInput("domain_transfer_request_fee.xml", substitutions);
|
||||
@@ -1551,32 +1445,12 @@ class DomainTransferRequestFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v06() {
|
||||
void testFailure_feeGivenInWrongScale_std_v1() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
CurrencyValueScaleException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_bad_scale.xml", FEE_06_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v11() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
CurrencyValueScaleException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_bad_scale.xml", FEE_11_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v12() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
CurrencyValueScaleException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_bad_scale.xml", FEE_12_MAP));
|
||||
() -> doFailingTest("domain_transfer_request_fee_bad_scale.xml", FEE_STD_1_0_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@@ -1594,21 +1468,9 @@ class DomainTransferRequestFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v06() {
|
||||
void testFailure_wrongFeeAmount_std_v1() {
|
||||
setupDomain("example", "tld");
|
||||
runWrongFeeAmountTest(FEE_06_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v11() {
|
||||
setupDomain("example", "tld");
|
||||
runWrongFeeAmountTest(FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v12() {
|
||||
setupDomain("example", "tld");
|
||||
runWrongFeeAmountTest(FEE_12_MAP);
|
||||
runWrongFeeAmountTest(FEE_STD_1_0_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1643,36 +1505,6 @@ class DomainTransferRequestFlowTest
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_badContactPassword() {
|
||||
setupDomain("example", "tld");
|
||||
// Change the contact's password so it does not match the password in the file.
|
||||
contact =
|
||||
persistResource(
|
||||
contact
|
||||
.asBuilder()
|
||||
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
|
||||
.build());
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
BadAuthInfoForResourceException.class,
|
||||
() -> doFailingTest("domain_transfer_request.xml"));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_badContactRepoId() {
|
||||
setupDomain("example", "tld");
|
||||
// Set the contact to a different ROID, but don't persist it; this is just so the substitution
|
||||
// code above will write the wrong ROID into the file.
|
||||
contact = contact.asBuilder().setRepoId("DEADBEEF_TLD-ROID").build();
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
BadAuthInfoForResourceException.class,
|
||||
() -> doFailingTest("domain_transfer_request.xml"));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_clientApproved() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
@@ -1776,7 +1608,6 @@ class DomainTransferRequestFlowTest
|
||||
setEppInput(
|
||||
"domain_transfer_request_wildcard.xml",
|
||||
ImmutableMap.of("YEARS", "1", "DOMAIN", "--invalid", "EXDATE", "2002-09-08T22:00:00.0Z"));
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
assertMutatingFlow(true);
|
||||
ResourceDoesNotExistException thrown =
|
||||
assertThrows(
|
||||
@@ -1788,7 +1619,6 @@ class DomainTransferRequestFlowTest
|
||||
@Test
|
||||
void testFailure_nonexistentDomain() {
|
||||
createTld("tld");
|
||||
contact = persistActiveContact("jd1234");
|
||||
ResourceDoesNotExistException thrown =
|
||||
assertThrows(
|
||||
ResourceDoesNotExistException.class,
|
||||
@@ -1796,6 +1626,22 @@ class DomainTransferRequestFlowTest
|
||||
assertThat(thrown).hasMessageThat().contains(String.format("(%s)", "example.tld"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_cannotUseContactAuthInfo() {
|
||||
// RFC 5731: "An OPTIONAL "roid" attribute MUST be used to identify the registrant or contact
|
||||
// object if and only if the given authInfo is associated with a registrant or contact object,
|
||||
// and not the domain object itself."
|
||||
//
|
||||
// We have no contacts, so it cannot be valid to specify a roid
|
||||
setupDomain("example", "tld");
|
||||
assertAboutEppExceptions()
|
||||
.that(
|
||||
assertThrows(
|
||||
BadAuthInfoForResourceException.class,
|
||||
() -> doFailingTest("domain_transfer_request_contact_auth_info_failure.xml")))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_periodInMonths() {
|
||||
setupDomain("example", "tld");
|
||||
@@ -1969,4 +1815,250 @@ class DomainTransferRequestFlowTest
|
||||
assertThrows(AlreadyRedeemedAllocationTokenException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v06() {
|
||||
setupDomain("example", "tld");
|
||||
runWrongFeeAmountTest(FEE_06_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v11() {
|
||||
setupDomain("example", "tld");
|
||||
runWrongFeeAmountTest(FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v12() {
|
||||
setupDomain("example", "tld");
|
||||
runWrongFeeAmountTest(FEE_12_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v06() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_applied.xml", FEE_06_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v11() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_applied.xml", FEE_11_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appliedFee_v12() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_applied.xml", FEE_12_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v06() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_grace_period.xml", FEE_06_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v11() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_grace_period.xml", FEE_11_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_gracePeriodFee_v12() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_grace_period.xml", FEE_12_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v06() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_request_fee_defaults.xml",
|
||||
"domain_transfer_request_response_fee.xml",
|
||||
FEE_06_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v11() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_request_fee_defaults.xml",
|
||||
"domain_transfer_request_response_fee.xml",
|
||||
FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_withDefaultAttributes_v12() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_request_fee_defaults.xml",
|
||||
"domain_transfer_request_response_fee.xml",
|
||||
FEE_12_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v06() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_refundable.xml", FEE_06_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v11() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_refundable.xml", FEE_11_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_refundableFee_v12() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
UnsupportedFeeAttributeException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_refundable.xml", FEE_12_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v06() {
|
||||
runWrongCurrencyTest(FEE_06_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v11() {
|
||||
runWrongCurrencyTest(FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v12() {
|
||||
runWrongCurrencyTest(FEE_12_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v06() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
CurrencyValueScaleException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_bad_scale.xml", FEE_06_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v11() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
CurrencyValueScaleException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_bad_scale.xml", FEE_11_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_feeGivenInWrongScale_v12() {
|
||||
setupDomain("example", "tld");
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
CurrencyValueScaleException.class,
|
||||
() -> doFailingTest("domain_transfer_request_fee_bad_scale.xml", FEE_12_MAP));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v06() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_request_fee.xml", "domain_transfer_request_response_fee.xml", FEE_06_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v11() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_request_fee.xml", "domain_transfer_request_response_fee.xml", FEE_11_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fee_v12() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_request_fee.xml", "domain_transfer_request_response_fee.xml", FEE_12_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_customLogicFee_v06() throws Exception {
|
||||
setupDomain("expensive-domain", "foo");
|
||||
clock.advanceOneMilli();
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_request_separate_fees.xml",
|
||||
"domain_transfer_request_response_fees.xml",
|
||||
domain.getRegistrationExpirationTime().plusYears(1),
|
||||
new ImmutableMap.Builder<String, String>()
|
||||
.put("DOMAIN", "expensive-domain.foo")
|
||||
.put("YEARS", "1")
|
||||
.put("AMOUNT", "111.00")
|
||||
.put("EXDATE", "2002-09-08T22:00:00.0Z")
|
||||
.put("FEE_VERSION", "fee-0.6")
|
||||
.put("FEE_NS", "fee")
|
||||
.build(),
|
||||
Optional.of(Money.of(USD, 111)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_premiumNotBlocked_v12() throws Exception {
|
||||
setupDomain("rich", "example");
|
||||
clock.advanceOneMilli();
|
||||
// We don't verify the results; just check that the flow doesn't fail.
|
||||
runTest("domain_transfer_request_fee.xml", UserPrivileges.NORMAL, RICH_DOMAIN_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_superuserExtension_zeroPeriod_feeTransferExtension_v12() {
|
||||
setupDomain("example", "tld");
|
||||
eppRequestSource = EppRequestSource.TOOL;
|
||||
clock.advanceOneMilli();
|
||||
assertThrows(
|
||||
TransferPeriodZeroAndFeeTransferExtensionException.class,
|
||||
() ->
|
||||
runTest(
|
||||
"domain_transfer_request_fee_and_superuser_extension.xml",
|
||||
UserPrivileges.SUPERUSER,
|
||||
new ImmutableMap.Builder<String, String>()
|
||||
.putAll(FEE_12_MAP)
|
||||
.put("PERIOD", "0")
|
||||
.put("AUTOMATIC_TRANSFER_LENGTH", "5")
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,8 +63,6 @@ import google.registry.config.RegistryConfig;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.UnimplementedExtensionException;
|
||||
import google.registry.flows.EppRequestSource;
|
||||
import google.registry.flows.FlowTestCase.CommitMode;
|
||||
import google.registry.flows.FlowTestCase.UserPrivileges;
|
||||
import google.registry.flows.FlowUtils.NotLoggedInException;
|
||||
import google.registry.flows.ResourceFlowTestCase;
|
||||
import google.registry.flows.ResourceFlowUtils.AddExistingValueException;
|
||||
@@ -74,7 +72,6 @@ import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
|
||||
import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
|
||||
import google.registry.flows.ResourceFlowUtils.StatusNotClientSettableException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.EmptySecDnsUpdateException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.FeesMismatchException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.FeesRequiredForNonFreeOperationException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.InvalidDsRecordException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.LinkedResourceInPendingDeleteProhibitsOperationException;
|
||||
@@ -95,9 +92,6 @@ import google.registry.flows.exceptions.ResourceStatusProhibitsOperationExceptio
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.DesignatedContact.Type;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainAuthInfo;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
@@ -132,14 +126,9 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
"DIGEST_TYPE", "1",
|
||||
"DIGEST", "A94A8FE5CCB19BA61C4C0873D391E987982FBBD3");
|
||||
|
||||
private Contact sh8013Contact;
|
||||
private Contact mak21Contact;
|
||||
private Contact unusedContact;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
// Note that "domain_update.xml" tests adding and removing the same contact type.
|
||||
setEppInput("domain_update.xml");
|
||||
}
|
||||
|
||||
@@ -147,33 +136,6 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
for (int i = 1; i <= 14; ++i) {
|
||||
persistActiveHost(String.format("ns%d.example.foo", i));
|
||||
}
|
||||
sh8013Contact = persistActiveContact("sh8013");
|
||||
mak21Contact = persistActiveContact("mak21");
|
||||
unusedContact = persistActiveContact("unused");
|
||||
}
|
||||
|
||||
private void persistDomainWithRegistrant() throws Exception {
|
||||
Host host = loadResource(Host.class, "ns1.example.foo", clock.nowUtc()).get();
|
||||
Domain domain =
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.TECH, mak21Contact.createVKey()),
|
||||
DesignatedContact.create(Type.ADMIN, mak21Contact.createVKey()),
|
||||
DesignatedContact.create(Type.BILLING, mak21Contact.createVKey())))
|
||||
.setRegistrant(Optional.of(mak21Contact.createVKey()))
|
||||
.setNameservers(ImmutableSet.of(host.createVKey()))
|
||||
.build());
|
||||
persistResource(
|
||||
new DomainHistory.Builder()
|
||||
.setType(DOMAIN_CREATE)
|
||||
.setModificationTime(clock.nowUtc())
|
||||
.setRegistrarId(domain.getCreationRegistrarId())
|
||||
.setDomain(domain)
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
}
|
||||
|
||||
private Domain persistDomain() throws Exception {
|
||||
@@ -182,10 +144,6 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.TECH, sh8013Contact.createVKey()),
|
||||
DesignatedContact.create(Type.ADMIN, unusedContact.createVKey())))
|
||||
.setNameservers(ImmutableSet.of(host.createVKey()))
|
||||
.build());
|
||||
persistResource(
|
||||
@@ -314,6 +272,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
|
||||
@Test
|
||||
void testFailure_minimumDataset_whenAddingNewContacts() throws Exception {
|
||||
persistActiveContact("mak21");
|
||||
// This EPP adds a new technical contact mak21 that wasn't already present.
|
||||
setEppInput("domain_update_empty_registrant.xml");
|
||||
persistReferencedEntities();
|
||||
@@ -352,7 +311,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
persistReferencedEntities();
|
||||
persistDomain();
|
||||
setEppInput("domain_update_max_everything.xml");
|
||||
// Create 26 hosts and 8 contacts. Start the domain with half of them.
|
||||
// Create 26 hosts. Start the domain with half of them.
|
||||
ImmutableSet.Builder<VKey<Host>> nameservers = new ImmutableSet.Builder<>();
|
||||
for (int i = 0; i < 26; i++) {
|
||||
Host host = persistActiveHost(String.format("max_test_%d.example.tld", i));
|
||||
@@ -361,10 +320,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
}
|
||||
}
|
||||
persistResource(
|
||||
reloadResourceByForeignKey()
|
||||
.asBuilder()
|
||||
.setNameservers(nameservers.build())
|
||||
.build());
|
||||
reloadResourceByForeignKey().asBuilder().setNameservers(nameservers.build()).build());
|
||||
clock.advanceOneMilli();
|
||||
assertMutatingFlow(true);
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
@@ -438,22 +394,9 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
assertThat(addedHost.getSuperordinateDomain()).isEqualTo(domain.createVKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_registrantMovedToTechContact() throws Exception {
|
||||
setEppInput("domain_update_registrant_to_tech.xml");
|
||||
persistReferencedEntities();
|
||||
Contact sh8013 = loadResource(Contact.class, "sh8013", clock.nowUtc()).get();
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setRegistrant(Optional.of(sh8013.createVKey()))
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_removeClientUpdateProhibited() throws Exception {
|
||||
setEppInput("domain_update_remove_client_update_prohibited.xml");
|
||||
persistReferencedEntities();
|
||||
persistResource(
|
||||
persistDomain()
|
||||
@@ -549,21 +492,26 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_secDnsAddSameDoesNothing() throws Exception {
|
||||
doSecDnsSuccessfulTest(
|
||||
"domain_update_dsdata_add.xml",
|
||||
ImmutableSet.of(SOME_DSDATA),
|
||||
ImmutableSet.of(SOME_DSDATA),
|
||||
ImmutableMap.of(
|
||||
"KEY_TAG",
|
||||
"1",
|
||||
"ALG",
|
||||
"2",
|
||||
"DIGEST_TYPE",
|
||||
"2",
|
||||
"DIGEST",
|
||||
"9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"),
|
||||
false);
|
||||
void testFailure_secDnsAddSame_throwsException() throws Exception {
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
AddExistingValueException.class,
|
||||
() ->
|
||||
doSecDnsSuccessfulTest(
|
||||
"domain_update_dsdata_add.xml",
|
||||
ImmutableSet.of(SOME_DSDATA),
|
||||
ImmutableSet.of(SOME_DSDATA),
|
||||
ImmutableMap.of(
|
||||
"KEY_TAG",
|
||||
"1",
|
||||
"ALG",
|
||||
"2",
|
||||
"DIGEST_TYPE",
|
||||
"2",
|
||||
"DIGEST",
|
||||
"9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"),
|
||||
false));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -681,7 +629,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
2,
|
||||
2,
|
||||
base16()
|
||||
.decode("9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"))),
|
||||
.decode("0F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"))),
|
||||
ImmutableMap.of(
|
||||
"KEY_TAG",
|
||||
"1",
|
||||
@@ -690,8 +638,8 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
"DIGEST_TYPE",
|
||||
"2",
|
||||
"DIGEST",
|
||||
"9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"),
|
||||
false);
|
||||
"0F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"),
|
||||
true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -799,29 +747,43 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_secDnsAddRemoveSame() throws Exception {
|
||||
// Adding and removing the same dsData is a no-op because removes are processed first.
|
||||
doSecDnsSuccessfulTest(
|
||||
"domain_update_dsdata_add_rem_same.xml",
|
||||
ImmutableSet.of(
|
||||
SOME_DSDATA,
|
||||
DomainDsData.create(
|
||||
12345, 3, 1, base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
|
||||
ImmutableSet.of(
|
||||
SOME_DSDATA,
|
||||
DomainDsData.create(
|
||||
12345, 3, 1, base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
|
||||
false);
|
||||
void testFailure_secDnsAddRemoveSame_throwsException() throws Exception {
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
AddRemoveSameValueException.class,
|
||||
() ->
|
||||
doSecDnsSuccessfulTest(
|
||||
"domain_update_dsdata_add_rem_same.xml",
|
||||
ImmutableSet.of(
|
||||
SOME_DSDATA,
|
||||
DomainDsData.create(
|
||||
12345,
|
||||
3,
|
||||
1,
|
||||
base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
|
||||
ImmutableSet.of(
|
||||
SOME_DSDATA,
|
||||
DomainDsData.create(
|
||||
12345,
|
||||
3,
|
||||
1,
|
||||
base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
|
||||
false));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_secDnsRemoveAlreadyNotThere() throws Exception {
|
||||
// Removing a dsData that isn't there is a no-op.
|
||||
doSecDnsSuccessfulTest(
|
||||
"domain_update_dsdata_rem.xml",
|
||||
ImmutableSet.of(SOME_DSDATA),
|
||||
ImmutableSet.of(SOME_DSDATA),
|
||||
false);
|
||||
void testFailure_secDnsRemoveAlreadyNotThere_throwsException() throws Exception {
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
RemoveNonexistentValueException.class,
|
||||
() ->
|
||||
doSecDnsSuccessfulTest(
|
||||
"domain_update_dsdata_rem.xml",
|
||||
ImmutableSet.of(SOME_DSDATA),
|
||||
ImmutableSet.of(SOME_DSDATA),
|
||||
false));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
void doServerStatusBillingTest(String xmlFilename, boolean isBillable) throws Exception {
|
||||
@@ -856,14 +818,6 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
doServerStatusBillingTest("domain_update_add_server_status.xml", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_noBillingOnPreExistingServerStatus() throws Exception {
|
||||
eppRequestSource = EppRequestSource.TOOL;
|
||||
Domain addStatusDomain = persistActiveDomain(getUniqueIdFromCommand());
|
||||
persistResource(addStatusDomain.asBuilder().addStatusValue(SERVER_RENEW_PROHIBITED).build());
|
||||
doServerStatusBillingTest("domain_update_add_server_status.xml", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_removeServerStatusBillingEvent() throws Exception {
|
||||
eppRequestSource = EppRequestSource.TOOL;
|
||||
@@ -1101,8 +1055,6 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
@Test
|
||||
void testFailure_missingHost() throws Exception {
|
||||
persistActiveHost("ns1.example.foo");
|
||||
persistActiveContact("sh8013");
|
||||
persistActiveContact("mak21");
|
||||
persistActiveDomain(getUniqueIdFromCommand());
|
||||
LinkedResourcesDoNotExistException thrown =
|
||||
assertThrows(LinkedResourcesDoNotExistException.class, this::runFlow);
|
||||
@@ -1390,7 +1342,20 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_removeNonexistentValue() throws Exception {
|
||||
void testFailure_addExistingStatusValue() throws Exception {
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setStatusValues(ImmutableSet.of(CLIENT_RENEW_PROHIBITED))
|
||||
.build());
|
||||
setEppInput("domain_update_add_non_server_status.xml");
|
||||
assertAboutEppExceptions()
|
||||
.that(assertThrows(AddExistingValueException.class, this::runFlow))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_removeNonexistentNameserver() throws Exception {
|
||||
persistActiveDomain(getUniqueIdFromCommand());
|
||||
persistActiveHost("ns1.example.foo");
|
||||
setEppInput("domain_update_remove_nameserver.xml");
|
||||
@@ -1399,16 +1364,22 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_removeNonexistentStatusValue() throws Exception {
|
||||
persistActiveDomain(getUniqueIdFromCommand());
|
||||
setEppInput("domain_update_remove_client_update_prohibited.xml");
|
||||
assertAboutEppExceptions()
|
||||
.that(assertThrows(RemoveNonexistentValueException.class, this::runFlow))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_minimumDataset_addingNewRegistrantFails() throws Exception {
|
||||
persistActiveContact("sh8013");
|
||||
persistReferencedEntities();
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, sh8013Contact.createVKey()),
|
||||
DesignatedContact.create(Type.TECH, sh8013Contact.createVKey())))
|
||||
.setRegistrant(Optional.empty())
|
||||
.build());
|
||||
// This EPP sets the registrant to sh8013, whereas in our test setup it is absent.
|
||||
@@ -1502,15 +1473,6 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
assertAboutDomains().that(reloadResourceByForeignKey()).hasExactlyStatusValues(CLIENT_HOLD);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_freePremium_wrongFee() throws Exception {
|
||||
setEppInput("domain_update_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11"));
|
||||
persistReferencedEntities();
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
// This test should throw an exception, because the fee extension is required when the fee is not
|
||||
// zero.
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright 2026 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.domain;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import google.registry.model.eppcommon.ProtocolDefinition;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Class for testing the XML extension definitions loaded in the prod environment. */
|
||||
public class ProductionSimulatingFeeExtensionsTest {
|
||||
|
||||
private RegistryEnvironment previousEnvironment;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
previousEnvironment = RegistryEnvironment.get();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() {
|
||||
previousEnvironment.setup();
|
||||
ProtocolDefinition.reloadServiceExtensionUris();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNonProdEnvironments() {
|
||||
for (RegistryEnvironment env : RegistryEnvironment.values()) {
|
||||
if (env.equals(RegistryEnvironment.PRODUCTION)) {
|
||||
continue;
|
||||
}
|
||||
env.setup();
|
||||
ProtocolDefinition.reloadServiceExtensionUris();
|
||||
assertThat(ProtocolDefinition.getVisibleServiceExtensionUris())
|
||||
.containsExactly(
|
||||
"urn:ietf:params:xml:ns:launch-1.0",
|
||||
"urn:ietf:params:xml:ns:rgp-1.0",
|
||||
"urn:ietf:params:xml:ns:secDNS-1.1",
|
||||
"urn:ietf:params:xml:ns:fee-0.6",
|
||||
"urn:ietf:params:xml:ns:fee-0.11",
|
||||
"urn:ietf:params:xml:ns:fee-0.12",
|
||||
"urn:ietf:params:xml:ns:epp:fee-1.0");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProdEnvironment() {
|
||||
RegistryEnvironment.PRODUCTION.setup();
|
||||
ProtocolDefinition.reloadServiceExtensionUris();
|
||||
// prod shouldn't have the fee extension version 1.0
|
||||
assertThat(ProtocolDefinition.getVisibleServiceExtensionUris())
|
||||
.containsExactly(
|
||||
"urn:ietf:params:xml:ns:launch-1.0",
|
||||
"urn:ietf:params:xml:ns:rgp-1.0",
|
||||
"urn:ietf:params:xml:ns:secDNS-1.1",
|
||||
"urn:ietf:params:xml:ns:fee-0.6",
|
||||
"urn:ietf:params:xml:ns:fee-0.11",
|
||||
"urn:ietf:params:xml:ns:fee-0.12");
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@ import google.registry.flows.host.HostFlowUtils.HostNameNotNormalizedException;
|
||||
import google.registry.flows.host.HostFlowUtils.HostNameNotPunyCodedException;
|
||||
import google.registry.flows.host.HostFlowUtils.HostNameTooLongException;
|
||||
import google.registry.flows.host.HostFlowUtils.HostNameTooShallowException;
|
||||
import google.registry.flows.host.HostFlowUtils.InvalidHostNameException;
|
||||
import google.registry.flows.host.HostFlowUtils.LoopbackIpNotValidForHostException;
|
||||
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainDoesNotExistException;
|
||||
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainInPendingDeleteException;
|
||||
@@ -82,9 +83,14 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, Host> {
|
||||
}
|
||||
|
||||
private void doSuccessfulTest() throws Exception {
|
||||
doSuccessfulTest("host_create_response.xml", ImmutableMap.of());
|
||||
}
|
||||
|
||||
private void doSuccessfulTest(String responseFile, ImmutableMap<String, String> substitutions)
|
||||
throws Exception {
|
||||
clock.advanceOneMilli();
|
||||
assertMutatingFlow(true);
|
||||
runFlowAssertResponse(loadFile("host_create_response.xml"));
|
||||
runFlowAssertResponse(loadFile(responseFile, substitutions));
|
||||
Host host = reloadResourceByForeignKey();
|
||||
// Check that the host was created and persisted with a history entry.
|
||||
assertAboutHosts()
|
||||
@@ -134,6 +140,28 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, Host> {
|
||||
assertHostDnsRequests("ns1.example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_tldWithHyphenOn3And4() throws Exception {
|
||||
setEppHostCreateInput("ns1.example.zz--main-2262", null);
|
||||
doSuccessfulTest(
|
||||
"host_create_response_wildcard.xml",
|
||||
ImmutableMap.of("HOSTNAME", "ns1.example.zz--main-2262"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_domainWithHyphenOn3And4() throws Exception {
|
||||
setEppHostCreateInput("ns1.zz--main-2262.tld", null);
|
||||
EppException thrown = assertThrows(InvalidHostNameException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_hostnameWithHyphenOn3And4() throws Exception {
|
||||
setEppHostCreateInput("zz--ns1.domain.tld", null);
|
||||
EppException thrown = assertThrows(InvalidHostNameException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_multipartTLDsAndInvalidHost() {
|
||||
createTlds("bar.tld", "tld");
|
||||
|
||||
@@ -17,7 +17,6 @@ package google.registry.flows.poll;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.createHistoryEntryForEppResource;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
@@ -28,7 +27,6 @@ import google.registry.flows.poll.PollAckFlow.InvalidMessageIdException;
|
||||
import google.registry.flows.poll.PollAckFlow.MessageDoesNotExistException;
|
||||
import google.registry.flows.poll.PollAckFlow.MissingMessageIdException;
|
||||
import google.registry.flows.poll.PollAckFlow.NotAuthorizedToAckMessageException;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
@@ -43,7 +41,6 @@ class PollAckFlowTest extends FlowTestCase<PollAckFlow> {
|
||||
private static final long MESSAGE_ID = 3;
|
||||
|
||||
private Domain domain;
|
||||
private Contact contact;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
@@ -51,8 +48,7 @@ class PollAckFlowTest extends FlowTestCase<PollAckFlow> {
|
||||
clock.setTo(DateTime.parse("2011-01-02T01:01:01Z"));
|
||||
setRegistrarIdForFlow("NewRegistrar");
|
||||
createTld("example");
|
||||
contact = persistActiveContact("jd1234");
|
||||
domain = persistResource(DatabaseHelper.newDomain("test.example", contact));
|
||||
domain = persistResource(DatabaseHelper.newDomain("test.example"));
|
||||
}
|
||||
|
||||
private void persistOneTimePollMessage(long messageId) {
|
||||
@@ -85,43 +81,6 @@ class PollAckFlowTest extends FlowTestCase<PollAckFlow> {
|
||||
dryRunFlowAssertResponse(loadFile("poll_ack_response_empty.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_contactPollMessage() throws Exception {
|
||||
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "3-2011"));
|
||||
persistResource(
|
||||
new PollMessage.OneTime.Builder()
|
||||
.setId(MESSAGE_ID)
|
||||
.setRegistrarId(getRegistrarIdForFlow())
|
||||
.setEventTime(clock.nowUtc().minusDays(1))
|
||||
.setMsg("Some poll message.")
|
||||
.setHistoryEntry(createHistoryEntryForEppResource(contact))
|
||||
.build());
|
||||
assertMutatingFlow(true);
|
||||
runFlowAssertResponse(loadFile("poll_ack_response_empty.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_contactPollMessage_withIncorrectYearField() throws Exception {
|
||||
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "3-1999"));
|
||||
persistResource(
|
||||
new PollMessage.OneTime.Builder()
|
||||
.setId(MESSAGE_ID)
|
||||
.setRegistrarId(getRegistrarIdForFlow())
|
||||
.setEventTime(clock.nowUtc().minusDays(1))
|
||||
.setMsg("Some poll message.")
|
||||
.setHistoryEntry(createHistoryEntryForEppResource(contact))
|
||||
.build());
|
||||
assertMutatingFlow(true);
|
||||
assertThrows(MessageDoesNotExistException.class, this::runFlow);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_messageOnContact() throws Exception {
|
||||
persistOneTimePollMessage(MESSAGE_ID);
|
||||
assertMutatingFlow(true);
|
||||
runFlowAssertResponse(loadFile("poll_ack_response_empty.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_recentActiveAutorenew() throws Exception {
|
||||
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "3-2010"));
|
||||
@@ -184,21 +143,6 @@ class PollAckFlowTest extends FlowTestCase<PollAckFlow> {
|
||||
assertThrows(InvalidMessageIdException.class, this::runFlow);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_contactPollMessage_withMissingYearField() throws Exception {
|
||||
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "3"));
|
||||
persistResource(
|
||||
new PollMessage.OneTime.Builder()
|
||||
.setId(MESSAGE_ID)
|
||||
.setRegistrarId(getRegistrarIdForFlow())
|
||||
.setEventTime(clock.nowUtc().minusDays(1))
|
||||
.setMsg("Some poll message.")
|
||||
.setHistoryEntry(createHistoryEntryForEppResource(contact))
|
||||
.build());
|
||||
assertMutatingFlow(true);
|
||||
assertThrows(InvalidMessageIdException.class, this::runFlow);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_invalidId_stringInsteadOfNumeric() throws Exception {
|
||||
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "ABC-12345"));
|
||||
|
||||
@@ -16,7 +16,6 @@ package google.registry.flows.poll;
|
||||
|
||||
import static google.registry.testing.DatabaseHelper.createHistoryEntryForEppResource;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistNewRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
@@ -27,8 +26,6 @@ import com.google.common.collect.ImmutableList;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.FlowTestCase;
|
||||
import google.registry.flows.poll.PollRequestFlow.UnexpectedMessageIdException;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.host.Host;
|
||||
@@ -36,7 +33,6 @@ import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.transfer.TransferResponse.ContactTransferResponse;
|
||||
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
@@ -48,7 +44,6 @@ import org.junit.jupiter.api.Test;
|
||||
class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
||||
|
||||
private Domain domain;
|
||||
private Contact contact;
|
||||
private Host host;
|
||||
|
||||
@BeforeEach
|
||||
@@ -58,8 +53,7 @@ class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
||||
setRegistrarIdForFlow("NewRegistrar");
|
||||
createTld("example");
|
||||
persistNewRegistrar("BadRegistrar");
|
||||
contact = persistActiveContact("jd1234");
|
||||
domain = persistResource(DatabaseHelper.newDomain("test.example", contact));
|
||||
domain = persistResource(DatabaseHelper.newDomain("test.example"));
|
||||
host = persistActiveHost("ns1.test.example");
|
||||
}
|
||||
|
||||
@@ -99,31 +93,6 @@ class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
||||
runFlowAssertResponse(loadFile("poll_response_domain_transfer_no_cltrid.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_contactTransferPending() throws Exception {
|
||||
setRegistrarIdForFlow("TheRegistrar");
|
||||
persistResource(
|
||||
new PollMessage.OneTime.Builder()
|
||||
.setId(3L)
|
||||
.setRegistrarId(getRegistrarIdForFlow())
|
||||
.setEventTime(clock.nowUtc().minusDays(5))
|
||||
.setMsg("Transfer requested.")
|
||||
.setResponseData(
|
||||
ImmutableList.of(
|
||||
new ContactTransferResponse.Builder()
|
||||
.setContactId("sh8013")
|
||||
.setTransferStatus(TransferStatus.PENDING)
|
||||
.setGainingRegistrarId(getRegistrarIdForFlow())
|
||||
.setTransferRequestTime(clock.nowUtc().minusDays(5))
|
||||
.setLosingRegistrarId("NewRegistrar")
|
||||
.setPendingTransferExpirationTime(clock.nowUtc())
|
||||
.build()))
|
||||
.setHistoryEntry(createHistoryEntryForEppResource(contact))
|
||||
.build());
|
||||
assertMutatingFlow(false);
|
||||
runFlowAssertResponse(loadFile("poll_response_contact_transfer.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_domainPendingActionComplete() throws Exception {
|
||||
persistResource(
|
||||
@@ -225,29 +194,6 @@ class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
||||
runFlowAssertResponse(loadFile("poll_response_empty.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_contactDelete() throws Exception {
|
||||
// Contact delete poll messages do not have any response data, so ensure that no
|
||||
// response data block is produced in the poll message.
|
||||
HistoryEntry historyEntry =
|
||||
persistResource(
|
||||
new ContactHistory.Builder()
|
||||
.setRegistrarId("NewRegistrar")
|
||||
.setModificationTime(clock.nowUtc().minusDays(1))
|
||||
.setType(HistoryEntry.Type.CONTACT_DELETE)
|
||||
.setContact(contact)
|
||||
.build());
|
||||
persistResource(
|
||||
new PollMessage.OneTime.Builder()
|
||||
.setRegistrarId("NewRegistrar")
|
||||
.setMsg("Deleted contact jd1234")
|
||||
.setHistoryEntry(historyEntry)
|
||||
.setEventTime(clock.nowUtc().minusDays(1))
|
||||
.build());
|
||||
assertMutatingFlow(false);
|
||||
runFlowAssertResponse(loadFile("poll_response_contact_delete.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_hostDelete() throws Exception {
|
||||
// Host delete poll messages do not have any response data, so ensure that no
|
||||
|
||||
@@ -16,14 +16,17 @@ package google.registry.flows.session;
|
||||
|
||||
import static com.google.common.io.BaseEncoding.base64;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.PROHIBIT_CONTACT_OBJECTS_ON_LOGIN;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.deleteResource;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
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.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.UnimplementedExtensionException;
|
||||
import google.registry.flows.EppException.UnimplementedObjectServiceException;
|
||||
@@ -36,6 +39,8 @@ import google.registry.flows.session.LoginFlow.BadRegistrarIdException;
|
||||
import google.registry.flows.session.LoginFlow.RegistrarAccountNotActiveException;
|
||||
import google.registry.flows.session.LoginFlow.TooManyFailedLoginsException;
|
||||
import google.registry.flows.session.LoginFlow.UnsupportedLanguageException;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.common.FeatureFlag.FeatureStatus;
|
||||
import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.Registrar.State;
|
||||
@@ -56,6 +61,11 @@ public abstract class LoginFlowTestCase extends FlowTestCase<LoginFlow> {
|
||||
sessionMetadata.setRegistrarId(null); // Don't implicitly log in (all other flows need to).
|
||||
registrar = loadRegistrar("NewRegistrar");
|
||||
registrarBuilder = registrar.asBuilder();
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(PROHIBIT_CONTACT_OBJECTS_ON_LOGIN)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, FeatureStatus.ACTIVE))
|
||||
.build());
|
||||
}
|
||||
|
||||
// Can't inline this since it may be overridden in subclasses.
|
||||
@@ -117,6 +127,21 @@ public abstract class LoginFlowTestCase extends FlowTestCase<LoginFlow> {
|
||||
doFailingTest("login_invalid_extension.xml", UnimplementedExtensionException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_invalidContactObjectUri() {
|
||||
doFailingTest("login_with_contact_objuri.xml", UnimplementedObjectServiceException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_contactObjectUri_worksWhenNotProhibited() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(PROHIBIT_CONTACT_OBJECTS_ON_LOGIN)
|
||||
.asBuilder()
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, FeatureStatus.INACTIVE))
|
||||
.build());
|
||||
doSuccessfulTest("login_with_contact_objuri.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_invalidTypes() {
|
||||
doFailingTest("login_invalid_types.xml", UnimplementedObjectServiceException.class);
|
||||
|
||||
@@ -15,12 +15,10 @@
|
||||
package google.registry.model;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.testing.TestCacheExtension;
|
||||
import java.time.Duration;
|
||||
@@ -34,19 +32,6 @@ public class EppResourceTest extends EntityTestCase {
|
||||
public final TestCacheExtension testCacheExtension =
|
||||
new TestCacheExtension.Builder().withEppResourceCache(Duration.ofDays(1)).build();
|
||||
|
||||
@Test
|
||||
void test_loadByCacheIfEnabled_ignoresContactChange() {
|
||||
Contact originalContact = persistActiveContact("contact123");
|
||||
assertThat(EppResource.loadByCacheIfEnabled(ImmutableList.of(originalContact.createVKey())))
|
||||
.containsExactly(originalContact.createVKey(), originalContact);
|
||||
Contact modifiedContact =
|
||||
persistResource(originalContact.asBuilder().setEmailAddress("different@fake.lol").build());
|
||||
assertThat(EppResource.loadByCacheIfEnabled(ImmutableList.of(originalContact.createVKey())))
|
||||
.containsExactly(originalContact.createVKey(), originalContact);
|
||||
assertThat(ForeignKeyUtils.loadResource(Contact.class, "contact123", fakeClock.nowUtc()))
|
||||
.hasValue(modifiedContact);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_loadByCacheIfEnabled_ignoresHostChange() {
|
||||
Host originalHost = persistActiveHost("ns1.example.com");
|
||||
|
||||
@@ -16,7 +16,6 @@ package google.registry.model;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
@@ -24,7 +23,6 @@ import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
@@ -72,13 +70,6 @@ class ForeignKeyUtilsTest {
|
||||
.hasValue(domain.createVKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_loadContactKey() {
|
||||
Contact contact = persistActiveContact("john-doe");
|
||||
assertThat(ForeignKeyUtils.loadKey(Contact.class, "john-doe", fakeClock.nowUtc()))
|
||||
.hasValue(contact.createVKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_loadKeyMostRecentResource() {
|
||||
Host host = persistActiveHost("ns1.example.com");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user