mirror of
https://github.com/google/nomulus
synced 2026-05-25 01:01:57 +00:00
Compare commits
6 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4312322dc | ||
|
|
24dfaf6406 | ||
|
|
7afb8fa343 | ||
|
|
02b3f7b505 | ||
|
|
25342aa480 | ||
|
|
3ef1e6c6a4 |
@@ -25,6 +25,7 @@ import static google.registry.model.ResourceTransferUtils.handlePendingTransferO
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING;
|
||||
import static google.registry.model.transfer.TransferStatus.SERVER_CANCELLED;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.assertAsyncActionsAreAllowed;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -94,6 +95,7 @@ public final class ContactDeleteFlow implements TransactionalFlow {
|
||||
extensionManager.register(MetadataExtension.class);
|
||||
validateRegistrarIsLoggedIn(registrarId);
|
||||
extensionManager.validate();
|
||||
assertAsyncActionsAreAllowed();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
checkLinkedDomains(targetId, now, ContactResource.class, DomainBase::getReferencedContacts);
|
||||
ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now);
|
||||
|
||||
@@ -22,6 +22,7 @@ import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||
import static google.registry.flows.host.HostFlowUtils.validateHostName;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.assertAsyncActionsAreAllowed;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -96,6 +97,7 @@ public final class HostDeleteFlow implements TransactionalFlow {
|
||||
extensionManager.register(MetadataExtension.class);
|
||||
validateRegistrarIsLoggedIn(registrarId);
|
||||
extensionManager.validate();
|
||||
assertAsyncActionsAreAllowed();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
validateHostName(targetId);
|
||||
checkLinkedDomains(targetId, now, HostResource.class, DomainBase::getNameservers);
|
||||
|
||||
@@ -28,6 +28,7 @@ import static google.registry.flows.host.HostFlowUtils.verifySuperordinateDomain
|
||||
import static google.registry.flows.host.HostFlowUtils.verifySuperordinateDomainOwnership;
|
||||
import static google.registry.model.index.ForeignKeyIndex.loadAndGetKey;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.HOST_UPDATE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.assertAsyncActionsAreAllowed;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
|
||||
@@ -136,6 +137,9 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
validateHostName(targetId);
|
||||
HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now);
|
||||
boolean isHostRename = suppliedNewHostName != null;
|
||||
if (isHostRename) {
|
||||
assertAsyncActionsAreAllowed();
|
||||
}
|
||||
String oldHostName = targetId;
|
||||
String newHostName = firstNonNull(suppliedNewHostName, oldHostName);
|
||||
DomainBase oldSuperordinateDomain =
|
||||
|
||||
@@ -30,6 +30,7 @@ import google.registry.model.annotations.DeleteAfterMigration;
|
||||
import google.registry.model.common.TimedTransitionProperty.TimedTransition;
|
||||
import google.registry.model.replay.SqlOnlyEntity;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.PersistenceException;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -62,11 +63,28 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements
|
||||
* not the phase is read-only.
|
||||
*/
|
||||
public enum MigrationState {
|
||||
/** Datastore is the only DB being used. */
|
||||
DATASTORE_ONLY(PrimaryDatabase.DATASTORE, false, ReplayDirection.NO_REPLAY),
|
||||
|
||||
/** Datastore is the primary DB, with changes replicated to Cloud SQL. */
|
||||
DATASTORE_PRIMARY(PrimaryDatabase.DATASTORE, false, ReplayDirection.DATASTORE_TO_SQL),
|
||||
|
||||
/** Datastore is the primary DB, with replication, and async actions are disallowed. */
|
||||
DATASTORE_PRIMARY_NO_ASYNC(PrimaryDatabase.DATASTORE, false, ReplayDirection.DATASTORE_TO_SQL),
|
||||
|
||||
/** Datastore is the primary DB, with replication, and all mutating actions are disallowed. */
|
||||
DATASTORE_PRIMARY_READ_ONLY(PrimaryDatabase.DATASTORE, true, ReplayDirection.DATASTORE_TO_SQL),
|
||||
|
||||
/**
|
||||
* Cloud SQL is the primary DB, with replication back to Datastore, and all mutating actions are
|
||||
* disallowed.
|
||||
*/
|
||||
SQL_PRIMARY_READ_ONLY(PrimaryDatabase.CLOUD_SQL, true, ReplayDirection.SQL_TO_DATASTORE),
|
||||
|
||||
/** Cloud SQL is the primary DB, with changes replicated to Datastore. */
|
||||
SQL_PRIMARY(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.SQL_TO_DATASTORE),
|
||||
|
||||
/** Cloud SQL is the only DB being used. */
|
||||
SQL_ONLY(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY);
|
||||
|
||||
private final PrimaryDatabase primaryDatabase;
|
||||
@@ -146,11 +164,17 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements
|
||||
.putAll(
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
MigrationState.DATASTORE_ONLY,
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.putAll(
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
|
||||
MigrationState.DATASTORE_ONLY,
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.putAll(
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
|
||||
MigrationState.DATASTORE_ONLY,
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
|
||||
MigrationState.SQL_PRIMARY_READ_ONLY,
|
||||
MigrationState.SQL_PRIMARY)
|
||||
.putAll(
|
||||
@@ -165,10 +189,9 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements
|
||||
MigrationState.SQL_ONLY,
|
||||
MigrationState.SQL_PRIMARY_READ_ONLY,
|
||||
MigrationState.SQL_PRIMARY);
|
||||
|
||||
// In addition, we can always transition from a state to itself (useful when updating the map).
|
||||
for (MigrationState migrationState : MigrationState.values()) {
|
||||
builder.put(migrationState, migrationState);
|
||||
}
|
||||
Arrays.stream(MigrationState.values()).forEach(state -> builder.put(state, state));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -246,7 +269,7 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements
|
||||
* A provided map of transitions may be valid by itself (i.e. it shifts states properly, doesn't
|
||||
* skip states, and doesn't backtrack incorrectly) while still being invalid. In addition to the
|
||||
* transitions in the map being valid, the single transition from the current map at the current
|
||||
* time to the new map at the current time time must also be valid.
|
||||
* time to the new map at the current time must also be valid.
|
||||
*/
|
||||
private static void validateTransitionAtCurrentTime(
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition> newTransitions) {
|
||||
|
||||
@@ -70,8 +70,7 @@ public class ClaimsList extends ImmutableObject implements SqlOnlyEntity {
|
||||
*
|
||||
* <p>Note that the value of this field is parsed from the claims list file(See this <a
|
||||
* href="https://tools.ietf.org/html/draft-lozano-tmch-func-spec-08#section-6.1">RFC</>), it is
|
||||
* the DNL List creation datetime from the rfc. Since this field has been used by Datastore, we
|
||||
* cannot change its name until we finish the migration.
|
||||
* the DNL List creation datetime from the rfc.
|
||||
*
|
||||
* <p>TODO(b/177567432): Rename this field to tmdbGenerationTime.
|
||||
*/
|
||||
|
||||
@@ -47,7 +47,7 @@ public final class TmchCrl extends CrossTldSingleton implements SqlOnlyEntity {
|
||||
* Change the singleton to a new ASCII-armored X.509 CRL.
|
||||
*
|
||||
* <p>Please do not call this function unless your CRL is properly formatted, signed by the root,
|
||||
* and actually newer than the one currently in Datastore.
|
||||
* and actually newer than the one currently in the database.
|
||||
*/
|
||||
public static void set(final String crl, final String url) {
|
||||
jpaTm()
|
||||
|
||||
@@ -44,6 +44,7 @@ import google.registry.request.Modules.DatastoreServiceModule;
|
||||
import google.registry.request.Modules.Jackson2Module;
|
||||
import google.registry.request.Modules.NetHttpTransportModule;
|
||||
import google.registry.request.Modules.UrlConnectionServiceModule;
|
||||
import google.registry.request.Modules.UrlFetchServiceModule;
|
||||
import google.registry.request.Modules.UrlFetchTransportModule;
|
||||
import google.registry.request.Modules.UserServiceModule;
|
||||
import google.registry.request.auth.AuthModule;
|
||||
@@ -81,6 +82,7 @@ import javax.inject.Singleton;
|
||||
SheetsServiceModule.class,
|
||||
StackdriverModule.class,
|
||||
UrlConnectionServiceModule.class,
|
||||
UrlFetchServiceModule.class,
|
||||
UrlFetchTransportModule.class,
|
||||
UserServiceModule.class,
|
||||
VoidDnsWriterModule.class,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.persistence.transaction;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.DATASTORE_PRIMARY_NO_ASYNC;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
@@ -23,6 +24,7 @@ import com.google.appengine.api.utils.SystemProperty.Environment.Value;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Suppliers;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.annotations.DeleteAfterMigration;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.PrimaryDatabase;
|
||||
import google.registry.model.ofy.DatastoreTransactionManager;
|
||||
@@ -198,6 +200,22 @@ public final class TransactionManagerFactory {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that async actions (contact/host deletes and host renames) are allowed.
|
||||
*
|
||||
* <p>These are allowed at all times except during the {@link
|
||||
* DatabaseMigrationStateSchedule.MigrationState#DATASTORE_PRIMARY_NO_ASYNC} stage. Note that
|
||||
* {@link ReadOnlyModeException} may well be thrown during other read-only stages inside the
|
||||
* transaction manager; this method specifically checks only async actions.
|
||||
*/
|
||||
@DeleteAfterMigration
|
||||
public static void assertAsyncActionsAreAllowed() {
|
||||
if (DatabaseMigrationStateSchedule.getValueAtTime(DateTime.now(UTC))
|
||||
.equals(DATASTORE_PRIMARY_NO_ASYNC)) {
|
||||
throw new ReadOnlyModeException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Registry is currently undergoing maintenance and is in read-only mode. */
|
||||
public static class ReadOnlyModeException extends IllegalStateException {
|
||||
public ReadOnlyModeException() {
|
||||
|
||||
@@ -14,24 +14,25 @@
|
||||
|
||||
package google.registry.rde;
|
||||
|
||||
import static google.registry.request.UrlConnectionUtils.getResponseBytes;
|
||||
import static google.registry.request.UrlConnectionUtils.setBasicAuth;
|
||||
import static google.registry.request.UrlConnectionUtils.setPayload;
|
||||
import static com.google.appengine.api.urlfetch.FetchOptions.Builder.validateCertificate;
|
||||
import static com.google.appengine.api.urlfetch.HTTPMethod.PUT;
|
||||
import static com.google.common.io.BaseEncoding.base64;
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.api.client.http.HttpMethods;
|
||||
import com.google.appengine.api.urlfetch.HTTPHeader;
|
||||
import com.google.appengine.api.urlfetch.HTTPRequest;
|
||||
import com.google.appengine.api.urlfetch.HTTPResponse;
|
||||
import com.google.appengine.api.urlfetch.URLFetchService;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.keyring.api.KeyModule.Key;
|
||||
import google.registry.request.HttpException.InternalServerErrorException;
|
||||
import google.registry.request.UrlConnectionService;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.util.UrlConnectionException;
|
||||
import google.registry.xjc.XjcXmlTransformer;
|
||||
import google.registry.xjc.iirdea.XjcIirdeaResponseElement;
|
||||
import google.registry.xjc.iirdea.XjcIirdeaResult;
|
||||
@@ -39,7 +40,6 @@ import google.registry.xjc.rdeheader.XjcRdeHeader;
|
||||
import google.registry.xjc.rdereport.XjcRdeReportReport;
|
||||
import google.registry.xml.XmlException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
@@ -58,10 +58,10 @@ public class RdeReporter {
|
||||
* @see <a href="http://tools.ietf.org/html/draft-lozano-icann-registry-interfaces-05#section-4">
|
||||
* ICANN Registry Interfaces - Interface details</a>
|
||||
*/
|
||||
private static final MediaType MEDIA_TYPE = MediaType.XML_UTF_8;
|
||||
private static final String REPORT_MIME = "text/xml";
|
||||
|
||||
@Inject Retrier retrier;
|
||||
@Inject UrlConnectionService urlConnectionService;
|
||||
@Inject URLFetchService urlFetchService;
|
||||
|
||||
@Inject @Config("rdeReportUrlPrefix") String reportUrlPrefix;
|
||||
@Inject @Key("icannReportingPassword") String password;
|
||||
@@ -76,24 +76,29 @@ public class RdeReporter {
|
||||
// Send a PUT request to ICANN's HTTPS server.
|
||||
URL url = makeReportUrl(header.getTld(), report.getId());
|
||||
String username = header.getTld() + "_ry";
|
||||
String token = base64().encode(String.format("%s:%s", username, password).getBytes(UTF_8));
|
||||
final HTTPRequest req = new HTTPRequest(url, PUT, validateCertificate().setDeadline(60d));
|
||||
req.addHeader(new HTTPHeader(CONTENT_TYPE, REPORT_MIME));
|
||||
req.addHeader(new HTTPHeader(AUTHORIZATION, "Basic " + token));
|
||||
req.setPayload(reportBytes);
|
||||
logger.atInfo().log("Sending report:\n%s", new String(reportBytes, UTF_8));
|
||||
byte[] responseBytes =
|
||||
HTTPResponse rsp =
|
||||
retrier.callWithRetry(
|
||||
() -> {
|
||||
HttpURLConnection connection = urlConnectionService.createConnection(url);
|
||||
connection.setRequestMethod(HttpMethods.PUT);
|
||||
setBasicAuth(connection, username, password);
|
||||
setPayload(connection, reportBytes, MEDIA_TYPE.toString());
|
||||
int responseCode = connection.getResponseCode();
|
||||
if (responseCode == SC_OK || responseCode == SC_BAD_REQUEST) {
|
||||
return getResponseBytes(connection);
|
||||
HTTPResponse rsp1 = urlFetchService.fetch(req);
|
||||
switch (rsp1.getResponseCode()) {
|
||||
case SC_OK:
|
||||
case SC_BAD_REQUEST:
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("PUT failed");
|
||||
}
|
||||
throw new UrlConnectionException("PUT failed", connection);
|
||||
return rsp1;
|
||||
},
|
||||
SocketTimeoutException.class);
|
||||
|
||||
// Ensure the XML response is valid.
|
||||
XjcIirdeaResult result = parseResult(responseBytes);
|
||||
XjcIirdeaResult result = parseResult(rsp.getContent());
|
||||
if (result.getCode().getValue() != 1000) {
|
||||
logger.atWarning().log(
|
||||
"PUT rejected: %d %s\n%s",
|
||||
|
||||
@@ -32,6 +32,7 @@ import static java.util.Arrays.asList;
|
||||
import com.google.cloud.storage.BlobId;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.jcraft.jsch.JSch;
|
||||
@@ -122,7 +123,6 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
|
||||
@Inject @Key("rdeSigningKey") PGPKeyPair signingKey;
|
||||
@Inject @Key("rdeStagingDecryptionKey") PGPPrivateKey stagingDecryptionKey;
|
||||
@Inject RdeUploadAction() {}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
logger.atInfo().log("Attempting to acquire RDE upload lock for TLD '%s'.", tld);
|
||||
@@ -140,6 +140,27 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
|
||||
|
||||
@Override
|
||||
public void runWithLock(final DateTime watermark) throws Exception {
|
||||
// If a prefix is not provided, but we are in SQL mode, try to determine the prefix. This should
|
||||
// only happen when the RDE upload cron job runs to catch up any un-retried (i. e. expected)
|
||||
// RDE failures.
|
||||
if (prefix.isEmpty() && !tm().isOfy()) {
|
||||
// The prefix is always in the format of: rde-2022-02-21t00-00-00z-2022-02-21t00-07-33z, where
|
||||
// the first datetime is the watermark and the second one is the time when the RDE beam job
|
||||
// launched. We search for the latest folder that starts with "rde-[watermark]".
|
||||
String partialPrefix =
|
||||
String.format("rde-%s", watermark.toString("yyyy-MM-dd't'HH-mm-ss'z'"));
|
||||
String latestFilenameSuffix =
|
||||
gcsUtils.listFolderObjects(bucket, partialPrefix).stream()
|
||||
.max(Ordering.natural())
|
||||
.orElse(null);
|
||||
if (latestFilenameSuffix == null) {
|
||||
throw new NoContentException(
|
||||
String.format("RDE deposit for TLD %s on %s does not exist", tld, watermark));
|
||||
}
|
||||
int firstSlashPosition = latestFilenameSuffix.indexOf('/');
|
||||
prefix =
|
||||
Optional.of(partialPrefix + latestFilenameSuffix.substring(0, firstSlashPosition + 1));
|
||||
}
|
||||
logger.atInfo().log("Verifying readiness to upload the RDE deposit.");
|
||||
Optional<Cursor> cursor =
|
||||
transactIfJpaTm(() -> tm().loadByKeyIfPresent(Cursor.createVKey(RDE_STAGING, tld)));
|
||||
@@ -241,9 +262,9 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
|
||||
.setSignatureOutput(sigOut, signingKey)
|
||||
.setFileMetadata(nameWithoutPrefix, xmlLength, watermark)
|
||||
.build()) {
|
||||
long bytesCopied = ByteStreams.copy(ghostrydeDecoder, rydeEncoder);
|
||||
long bytesCopied = ByteStreams.copy(ghostrydeDecoder, rydeEncoder);
|
||||
logger.atInfo().log("Uploaded %,d bytes to path '%s'.", bytesCopied, rydeFilename);
|
||||
}
|
||||
}
|
||||
String sigFilename = nameWithoutPrefix + ".sig";
|
||||
BlobId sigGcsFilename = BlobId.of(bucket, name + ".sig");
|
||||
byte[] signature = sigOut.toByteArray();
|
||||
|
||||
@@ -23,14 +23,14 @@ import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
import com.google.api.client.json.JsonFactory;
|
||||
import com.google.api.client.json.jackson2.JacksonFactory;
|
||||
import com.google.appengine.api.datastore.DatastoreService;
|
||||
import com.google.appengine.api.urlfetch.URLFetchService;
|
||||
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;
|
||||
import com.google.appengine.api.users.UserService;
|
||||
import com.google.appengine.api.users.UserServiceFactory;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import java.net.HttpURLConnection;
|
||||
import javax.inject.Singleton;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
/** Dagger modules for App Engine services and other vendor classes. */
|
||||
public final class Modules {
|
||||
@@ -51,16 +51,18 @@ public final class Modules {
|
||||
public static final class UrlConnectionServiceModule {
|
||||
@Provides
|
||||
static UrlConnectionService provideUrlConnectionService() {
|
||||
return url -> {
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
if (connection instanceof HttpsURLConnection) {
|
||||
HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
|
||||
SSLContext tls13Context = SSLContext.getInstance("TLSv1.3");
|
||||
tls13Context.init(null, null, null);
|
||||
httpsConnection.setSSLSocketFactory(tls13Context.getSocketFactory());
|
||||
}
|
||||
return connection;
|
||||
};
|
||||
return url -> (HttpURLConnection) url.openConnection();
|
||||
}
|
||||
}
|
||||
|
||||
/** Dagger module for {@link URLFetchService}. */
|
||||
@Module
|
||||
public static final class UrlFetchServiceModule {
|
||||
private static final URLFetchService fetchService = URLFetchServiceFactory.getURLFetchService();
|
||||
|
||||
@Provides
|
||||
static URLFetchService provideUrlFetchService() {
|
||||
return fetchService;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,8 @@ import javax.inject.Inject;
|
||||
* Helper methods for accessing ICANN's TMCH root certificate and revocation list.
|
||||
*
|
||||
* <p>There are two CRLs, a real one for the production environment and a pilot one for
|
||||
* non-production environments. The Datastore singleton {@link TmchCrl} entity is used to cache this
|
||||
* CRL once loaded and will always contain the proper one corresponding to the environment.
|
||||
* non-production environments. The singleton {@link TmchCrl} entity is used to cache this CRL once
|
||||
* loaded and will always contain the proper one corresponding to the environment.
|
||||
*
|
||||
* <p>The CRTs do not change and are included as files in the codebase that are not refreshed. They
|
||||
* were downloaded from https://ca.icann.org/tmch.crt and https://ca.icann.org/tmch_pilot.crt
|
||||
@@ -66,7 +66,7 @@ public final class TmchCertificateAuthority {
|
||||
}
|
||||
|
||||
/**
|
||||
* A cached supplier that loads the CRL from Datastore or chooses a default value.
|
||||
* A cached supplier that loads the CRL from the database or chooses a default value.
|
||||
*
|
||||
* <p>We keep the cache here rather than caching TmchCrl in the model, because loading the CRL
|
||||
* string into an X509CRL instance is expensive and should itself be cached.
|
||||
@@ -132,7 +132,7 @@ public final class TmchCertificateAuthority {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update to the latest TMCH X.509 certificate revocation list and save it to Datastore.
|
||||
* Update to the latest TMCH X.509 certificate revocation list and save it to the database.
|
||||
*
|
||||
* <p>Your ASCII-armored CRL must be signed by the current ICANN root certificate.
|
||||
*
|
||||
|
||||
@@ -40,7 +40,7 @@ public final class TmchCrlAction implements Runnable {
|
||||
@Inject TmchCertificateAuthority tmchCertificateAuthority;
|
||||
@Inject TmchCrlAction() {}
|
||||
|
||||
/** Synchronously fetches latest ICANN TMCH CRL and saves it to Datastore. */
|
||||
/** Synchronously fetches latest ICANN TMCH CRL and saves it to the database. */
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
|
||||
@@ -40,6 +40,7 @@ import google.registry.rde.RdeModule;
|
||||
import google.registry.request.Modules.DatastoreServiceModule;
|
||||
import google.registry.request.Modules.Jackson2Module;
|
||||
import google.registry.request.Modules.UrlConnectionServiceModule;
|
||||
import google.registry.request.Modules.UrlFetchServiceModule;
|
||||
import google.registry.request.Modules.UserServiceModule;
|
||||
import google.registry.tools.AuthModule.LocalCredentialModule;
|
||||
import google.registry.tools.javascrap.CompareEscrowDepositsCommand;
|
||||
@@ -80,6 +81,7 @@ import javax.inject.Singleton;
|
||||
RequestFactoryModule.class,
|
||||
SecretManagerModule.class,
|
||||
UrlConnectionServiceModule.class,
|
||||
UrlFetchServiceModule.class,
|
||||
UserServiceModule.class,
|
||||
UtilsModule.class,
|
||||
VoidDnsWriterModule.class,
|
||||
|
||||
@@ -254,6 +254,15 @@ class ContactDeleteFlowTest extends ResourceFlowTestCase<ContactDeleteFlow, Cont
|
||||
assertIcannReportingActivityFieldLogged("srs-cont-delete");
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testModification_duringNoAsyncPhase() throws Exception {
|
||||
persistActiveContact(getUniqueIdFromCommand());
|
||||
DatabaseHelper.setMigrationScheduleToDatastorePrimaryNoAsync(clock);
|
||||
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
DatabaseHelper.removeDatabaseMigrationSchedule();
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testModification_duringReadOnlyPhase() throws Exception {
|
||||
persistActiveContact(getUniqueIdFromCommand());
|
||||
|
||||
@@ -840,6 +840,15 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
doSuccessfulTest();
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testSuccess_inNoAsyncPhase() throws Exception {
|
||||
DatabaseHelper.setMigrationScheduleToDatastorePrimaryNoAsync(clock);
|
||||
persistContactsAndHosts();
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_create_response_noasync.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
DatabaseHelper.removeDatabaseMigrationSchedule();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_maxNumberOfNameservers() throws Exception {
|
||||
setEppInput("domain_create_13_nameservers.xml");
|
||||
|
||||
@@ -358,6 +358,15 @@ class HostDeleteFlowTest extends ResourceFlowTestCase<HostDeleteFlow, HostResour
|
||||
DatabaseHelper.removeDatabaseMigrationSchedule();
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testModification_duringNoAsyncPhase() {
|
||||
persistActiveHost("ns1.example.tld");
|
||||
DatabaseHelper.setMigrationScheduleToDatastorePrimaryNoAsync(clock);
|
||||
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
DatabaseHelper.removeDatabaseMigrationSchedule();
|
||||
}
|
||||
|
||||
private void assertOfyDeleteSuccess(String registrarId, String clientTrid, boolean isSuperuser)
|
||||
throws Exception {
|
||||
HostResource deletedHost = reloadResourceByForeignKey();
|
||||
|
||||
@@ -1355,7 +1355,43 @@ class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, HostResour
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testModification_duringReadOnlyPhase() throws Exception {
|
||||
void testSuccess_nonHostRename_inNoAsyncPhase_succeeds() throws Exception {
|
||||
setEppInput("host_update_name_unchanged.xml");
|
||||
createTld("tld");
|
||||
DatabaseHelper.setMigrationScheduleToDatastorePrimaryNoAsync(clock);
|
||||
DomainBase domain = persistActiveDomain("example.tld");
|
||||
HostResource oldHost = persistActiveSubordinateHost(oldHostName(), domain);
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
// The example xml doesn't do a host rename, so reloading the host should work.
|
||||
assertAboutHosts()
|
||||
.that(reloadResourceByForeignKey())
|
||||
.hasLastSuperordinateChange(oldHost.getLastSuperordinateChange())
|
||||
.and()
|
||||
.hasSuperordinateDomain(domain.createVKey())
|
||||
.and()
|
||||
.hasPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.and()
|
||||
.hasLastTransferTime(null)
|
||||
.and()
|
||||
.hasOnlyOneHistoryEntryWhich()
|
||||
.hasType(HistoryEntry.Type.HOST_UPDATE);
|
||||
assertDnsTasksEnqueued("ns1.example.tld");
|
||||
DatabaseHelper.removeDatabaseMigrationSchedule();
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testRename_duringNoAsyncPhase_fails() throws Exception {
|
||||
createTld("tld");
|
||||
persistActiveSubordinateHost(oldHostName(), persistActiveDomain("example.tld"));
|
||||
DatabaseHelper.setMigrationScheduleToDatastorePrimaryNoAsync(clock);
|
||||
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
DatabaseHelper.removeDatabaseMigrationSchedule();
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testModification_duringReadOnlyPhase_fails() throws Exception {
|
||||
createTld("tld");
|
||||
persistActiveSubordinateHost(oldHostName(), persistActiveDomain("example.tld"));
|
||||
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.model.common;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.DATASTORE_ONLY;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.DATASTORE_PRIMARY;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.DATASTORE_PRIMARY_NO_ASYNC;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.DATASTORE_PRIMARY_READ_ONLY;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.SQL_ONLY;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.SQL_PRIMARY;
|
||||
@@ -71,10 +72,12 @@ public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
|
||||
runValidTransition(DATASTORE_ONLY, DATASTORE_PRIMARY);
|
||||
|
||||
runValidTransition(DATASTORE_PRIMARY, DATASTORE_ONLY);
|
||||
runValidTransition(DATASTORE_PRIMARY, DATASTORE_PRIMARY_READ_ONLY);
|
||||
runValidTransition(DATASTORE_PRIMARY, DATASTORE_PRIMARY_NO_ASYNC);
|
||||
runValidTransition(DATASTORE_PRIMARY_NO_ASYNC, DATASTORE_PRIMARY_READ_ONLY);
|
||||
|
||||
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, DATASTORE_ONLY);
|
||||
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, DATASTORE_PRIMARY);
|
||||
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, DATASTORE_PRIMARY_NO_ASYNC);
|
||||
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, SQL_PRIMARY_READ_ONLY);
|
||||
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, SQL_PRIMARY);
|
||||
|
||||
@@ -94,6 +97,7 @@ public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
|
||||
runInvalidTransition(DATASTORE_ONLY, SQL_PRIMARY);
|
||||
runInvalidTransition(DATASTORE_ONLY, SQL_ONLY);
|
||||
|
||||
runInvalidTransition(DATASTORE_PRIMARY, DATASTORE_PRIMARY_READ_ONLY);
|
||||
runInvalidTransition(DATASTORE_PRIMARY, SQL_PRIMARY_READ_ONLY);
|
||||
runInvalidTransition(DATASTORE_PRIMARY, SQL_PRIMARY);
|
||||
runInvalidTransition(DATASTORE_PRIMARY, SQL_ONLY);
|
||||
@@ -124,7 +128,8 @@ public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
|
||||
ImmutableSortedMap.<DateTime, MigrationState>naturalOrder()
|
||||
.put(START_OF_TIME, DATASTORE_ONLY)
|
||||
.put(startTime.plusHours(1), DATASTORE_PRIMARY)
|
||||
.put(startTime.plusHours(2), DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(startTime.plusHours(2), DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.put(startTime.plusHours(3), DATASTORE_PRIMARY_READ_ONLY)
|
||||
.build();
|
||||
assertThat(
|
||||
assertThrows(
|
||||
@@ -163,7 +168,8 @@ public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
|
||||
fakeClock.setTo(START_OF_TIME.plusDays(1));
|
||||
AllocationToken token =
|
||||
new AllocationToken.Builder().setToken("token").setTokenType(TokenType.SINGLE_USE).build();
|
||||
runValidTransition(DATASTORE_PRIMARY, DATASTORE_PRIMARY_READ_ONLY);
|
||||
runValidTransition(DATASTORE_PRIMARY, DATASTORE_PRIMARY_NO_ASYNC);
|
||||
runValidTransition(DATASTORE_PRIMARY_NO_ASYNC, DATASTORE_PRIMARY_READ_ONLY);
|
||||
assertThrows(ReadOnlyModeException.class, () -> persistResource(token));
|
||||
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, SQL_PRIMARY_READ_ONLY);
|
||||
assertThrows(ReadOnlyModeException.class, () -> persistResource(token));
|
||||
|
||||
@@ -366,8 +366,9 @@ public class ReplicateToDatastoreActionTest {
|
||||
ImmutableSortedMap.<DateTime, MigrationState>naturalOrder()
|
||||
.put(START_OF_TIME, MigrationState.DATASTORE_ONLY)
|
||||
.put(START_OF_TIME.plusHours(1), MigrationState.DATASTORE_PRIMARY)
|
||||
.put(START_OF_TIME.plusHours(2), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusHours(3), MigrationState.SQL_PRIMARY)
|
||||
.put(START_OF_TIME.plusHours(2), MigrationState.DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.put(START_OF_TIME.plusHours(3), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusHours(4), MigrationState.SQL_PRIMARY)
|
||||
.put(now.plusHours(1), MigrationState.SQL_PRIMARY_READ_ONLY)
|
||||
.put(now.plusHours(2), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(now.plusHours(3), MigrationState.DATASTORE_PRIMARY)
|
||||
|
||||
@@ -47,7 +47,9 @@ public class DatabaseMigrationScheduleTransitionConverterTest {
|
||||
MigrationState.DATASTORE_ONLY,
|
||||
DateTime.parse("2001-01-01T00:00:00.0Z"),
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
DateTime.parse("2002-01-01T00:00:00.0Z"),
|
||||
DateTime.parse("2002-01-01T01:00:00.0Z"),
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
|
||||
DateTime.parse("2002-01-01T02:00:00.0Z"),
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
|
||||
DateTime.parse("2002-01-02T00:00:00.0Z"),
|
||||
MigrationState.SQL_PRIMARY,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.rde;
|
||||
|
||||
import static com.google.appengine.api.urlfetch.HTTPMethod.PUT;
|
||||
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.Cursor.CursorType.RDE_REPORT;
|
||||
@@ -28,13 +29,20 @@ import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.joda.time.Duration.standardDays;
|
||||
import static org.joda.time.Duration.standardSeconds;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.appengine.api.urlfetch.HTTPHeader;
|
||||
import com.google.appengine.api.urlfetch.HTTPRequest;
|
||||
import com.google.appengine.api.urlfetch.HTTPResponse;
|
||||
import com.google.appengine.api.urlfetch.URLFetchService;
|
||||
import com.google.cloud.storage.BlobId;
|
||||
import com.google.cloud.storage.contrib.nio.testing.LocalStorageHelper;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.io.ByteSource;
|
||||
import google.registry.gcs.GcsUtils;
|
||||
import google.registry.model.common.Cursor;
|
||||
@@ -49,21 +57,20 @@ import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeKeyringModule;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.FakeSleeper;
|
||||
import google.registry.testing.FakeUrlConnectionService;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.xjc.XjcXmlTransformer;
|
||||
import google.registry.xjc.rdereport.XjcRdeReportReport;
|
||||
import google.registry.xml.XmlException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
/** Unit tests for {@link RdeReportAction}. */
|
||||
@DualDatabaseTest
|
||||
@@ -82,21 +89,20 @@ public class RdeReportActionTest {
|
||||
|
||||
private final FakeResponse response = new FakeResponse();
|
||||
private final EscrowTaskRunner runner = mock(EscrowTaskRunner.class);
|
||||
private final URLFetchService urlFetchService = mock(URLFetchService.class);
|
||||
private final ArgumentCaptor<HTTPRequest> request = ArgumentCaptor.forClass(HTTPRequest.class);
|
||||
private final HTTPResponse httpResponse = mock(HTTPResponse.class);
|
||||
private final PGPPublicKey encryptKey =
|
||||
new FakeKeyringModule().get().getRdeStagingEncryptionKey();
|
||||
private final GcsUtils gcsUtils = new GcsUtils(LocalStorageHelper.getOptions());
|
||||
private final BlobId reportFile =
|
||||
BlobId.of("tub", "test_2006-06-06_full_S1_R0-report.xml.ghostryde");
|
||||
private final HttpURLConnection httpUrlConnection = mock(HttpURLConnection.class);
|
||||
private final FakeUrlConnectionService urlConnectionService =
|
||||
new FakeUrlConnectionService(httpUrlConnection);
|
||||
private final ByteArrayOutputStream connectionOutputStream = new ByteArrayOutputStream();
|
||||
|
||||
private RdeReportAction createAction() {
|
||||
RdeReporter reporter = new RdeReporter();
|
||||
reporter.reportUrlPrefix = "https://rde-report.example";
|
||||
reporter.urlFetchService = urlFetchService;
|
||||
reporter.password = "foo";
|
||||
reporter.urlConnectionService = urlConnectionService;
|
||||
reporter.retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
|
||||
RdeReportAction action = new RdeReportAction();
|
||||
action.gcsUtils = gcsUtils;
|
||||
@@ -121,7 +127,6 @@ public class RdeReportActionTest {
|
||||
Cursor.create(RDE_UPLOAD, DateTime.parse("2006-06-07TZ"), Registry.get("test")));
|
||||
gcsUtils.createFromBytes(reportFile, Ghostryde.encode(REPORT_XML.read(), encryptKey));
|
||||
tm().transact(() -> RdeRevision.saveRevision("test", DateTime.parse("2006-06-06TZ"), FULL, 0));
|
||||
when(httpUrlConnection.getOutputStream()).thenReturn(connectionOutputStream);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
@@ -137,22 +142,24 @@ public class RdeReportActionTest {
|
||||
|
||||
@TestOfyAndSql
|
||||
void testRunWithLock() throws Exception {
|
||||
when(httpUrlConnection.getResponseCode()).thenReturn(SC_OK);
|
||||
when(httpUrlConnection.getInputStream()).thenReturn(IIRDEA_GOOD_XML.openStream());
|
||||
when(httpResponse.getResponseCode()).thenReturn(SC_OK);
|
||||
when(httpResponse.getContent()).thenReturn(IIRDEA_GOOD_XML.read());
|
||||
when(urlFetchService.fetch(request.capture())).thenReturn(httpResponse);
|
||||
createAction().runWithLock(loadRdeReportCursor());
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
|
||||
assertThat(response.getPayload()).isEqualTo("OK test 2006-06-06T00:00:00.000Z\n");
|
||||
|
||||
// Verify the HTTP request was correct.
|
||||
verify(httpUrlConnection).setRequestMethod("PUT");
|
||||
assertThat(httpUrlConnection.getURL().getProtocol()).isEqualTo("https");
|
||||
assertThat(httpUrlConnection.getURL().getPath()).endsWith("/test/20101017001");
|
||||
verify(httpUrlConnection).setRequestProperty("Content-Type", "text/xml; charset=utf-8");
|
||||
verify(httpUrlConnection).setRequestProperty("Authorization", "Basic dGVzdF9yeTpmb28=");
|
||||
assertThat(request.getValue().getMethod()).isSameInstanceAs(PUT);
|
||||
assertThat(request.getValue().getURL().getProtocol()).isEqualTo("https");
|
||||
assertThat(request.getValue().getURL().getPath()).endsWith("/test/20101017001");
|
||||
Map<String, String> headers = mapifyHeaders(request.getValue().getHeaders());
|
||||
assertThat(headers).containsEntry("CONTENT_TYPE", "text/xml");
|
||||
assertThat(headers).containsEntry("AUTHORIZATION", "Basic dGVzdF9yeTpmb28=");
|
||||
|
||||
// Verify the payload XML was the same as what's in testdata/report.xml.
|
||||
XjcRdeReportReport report = parseReport(connectionOutputStream.toByteArray());
|
||||
XjcRdeReportReport report = parseReport(request.getValue().getPayload());
|
||||
assertThat(report.getId()).isEqualTo("20101017001");
|
||||
assertThat(report.getCrDate()).isEqualTo(DateTime.parse("2010-10-17T00:15:00.0Z"));
|
||||
assertThat(report.getWatermark()).isEqualTo(DateTime.parse("2010-10-17T00:00:00Z"));
|
||||
@@ -160,8 +167,9 @@ public class RdeReportActionTest {
|
||||
|
||||
@TestOfyAndSql
|
||||
void testRunWithLock_withPrefix() throws Exception {
|
||||
when(httpUrlConnection.getResponseCode()).thenReturn(SC_OK);
|
||||
when(httpUrlConnection.getInputStream()).thenReturn(IIRDEA_GOOD_XML.openStream());
|
||||
when(httpResponse.getResponseCode()).thenReturn(SC_OK);
|
||||
when(httpResponse.getContent()).thenReturn(IIRDEA_GOOD_XML.read());
|
||||
when(urlFetchService.fetch(request.capture())).thenReturn(httpResponse);
|
||||
RdeReportAction action = createAction();
|
||||
action.prefix = Optional.of("job-name/");
|
||||
gcsUtils.delete(reportFile);
|
||||
@@ -174,14 +182,15 @@ public class RdeReportActionTest {
|
||||
assertThat(response.getPayload()).isEqualTo("OK test 2006-06-06T00:00:00.000Z\n");
|
||||
|
||||
// Verify the HTTP request was correct.
|
||||
verify(httpUrlConnection).setRequestMethod("PUT");
|
||||
assertThat(httpUrlConnection.getURL().getProtocol()).isEqualTo("https");
|
||||
assertThat(httpUrlConnection.getURL().getPath()).endsWith("/test/20101017001");
|
||||
verify(httpUrlConnection).setRequestProperty("Content-Type", "text/xml; charset=utf-8");
|
||||
verify(httpUrlConnection).setRequestProperty("Authorization", "Basic dGVzdF9yeTpmb28=");
|
||||
assertThat(request.getValue().getMethod()).isSameInstanceAs(PUT);
|
||||
assertThat(request.getValue().getURL().getProtocol()).isEqualTo("https");
|
||||
assertThat(request.getValue().getURL().getPath()).endsWith("/test/20101017001");
|
||||
Map<String, String> headers = mapifyHeaders(request.getValue().getHeaders());
|
||||
assertThat(headers).containsEntry("CONTENT_TYPE", "text/xml");
|
||||
assertThat(headers).containsEntry("AUTHORIZATION", "Basic dGVzdF9yeTpmb28=");
|
||||
|
||||
// Verify the payload XML was the same as what's in testdata/report.xml.
|
||||
XjcRdeReportReport report = parseReport(connectionOutputStream.toByteArray());
|
||||
XjcRdeReportReport report = parseReport(request.getValue().getPayload());
|
||||
assertThat(report.getId()).isEqualTo("20101017001");
|
||||
assertThat(report.getCrDate()).isEqualTo(DateTime.parse("2010-10-17T00:15:00.0Z"));
|
||||
assertThat(report.getWatermark()).isEqualTo(DateTime.parse("2010-10-17T00:00:00Z"));
|
||||
@@ -194,8 +203,9 @@ public class RdeReportActionTest {
|
||||
PGPPublicKey encryptKey = new FakeKeyringModule().get().getRdeStagingEncryptionKey();
|
||||
gcsUtils.createFromBytes(newReport, Ghostryde.encode(REPORT_XML.read(), encryptKey));
|
||||
tm().transact(() -> RdeRevision.saveRevision("test", DateTime.parse("2006-06-06TZ"), FULL, 1));
|
||||
when(httpUrlConnection.getResponseCode()).thenReturn(SC_OK);
|
||||
when(httpUrlConnection.getInputStream()).thenReturn(IIRDEA_GOOD_XML.openStream());
|
||||
when(httpResponse.getResponseCode()).thenReturn(SC_OK);
|
||||
when(httpResponse.getContent()).thenReturn(IIRDEA_GOOD_XML.read());
|
||||
when(urlFetchService.fetch(request.capture())).thenReturn(httpResponse);
|
||||
createAction().runWithLock(loadRdeReportCursor());
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
@@ -228,8 +238,9 @@ public class RdeReportActionTest {
|
||||
|
||||
@TestOfyAndSql
|
||||
void testRunWithLock_badRequest_throws500WithErrorInfo() throws Exception {
|
||||
when(httpUrlConnection.getResponseCode()).thenReturn(SC_BAD_REQUEST);
|
||||
when(httpUrlConnection.getInputStream()).thenReturn(IIRDEA_BAD_XML.openStream());
|
||||
when(httpResponse.getResponseCode()).thenReturn(SC_BAD_REQUEST);
|
||||
when(httpResponse.getContent()).thenReturn(IIRDEA_BAD_XML.read());
|
||||
when(urlFetchService.fetch(request.capture())).thenReturn(httpResponse);
|
||||
InternalServerErrorException thrown =
|
||||
assertThrows(
|
||||
InternalServerErrorException.class,
|
||||
@@ -240,17 +251,18 @@ public class RdeReportActionTest {
|
||||
@TestOfyAndSql
|
||||
void testRunWithLock_fetchFailed_throwsRuntimeException() throws Exception {
|
||||
class ExpectedThrownException extends RuntimeException {}
|
||||
when(httpUrlConnection.getResponseCode()).thenThrow(new ExpectedThrownException());
|
||||
when(urlFetchService.fetch(any(HTTPRequest.class))).thenThrow(new ExpectedThrownException());
|
||||
assertThrows(
|
||||
ExpectedThrownException.class, () -> createAction().runWithLock(loadRdeReportCursor()));
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testRunWithLock_socketTimeout_doesRetry() throws Exception {
|
||||
when(httpUrlConnection.getInputStream()).thenReturn(IIRDEA_GOOD_XML.openStream());
|
||||
when(httpUrlConnection.getResponseCode())
|
||||
when(httpResponse.getResponseCode()).thenReturn(SC_OK);
|
||||
when(httpResponse.getContent()).thenReturn(IIRDEA_GOOD_XML.read());
|
||||
when(urlFetchService.fetch(request.capture()))
|
||||
.thenThrow(new SocketTimeoutException())
|
||||
.thenReturn(SC_OK);
|
||||
.thenReturn(httpResponse);
|
||||
createAction().runWithLock(loadRdeReportCursor());
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
|
||||
@@ -261,6 +273,14 @@ public class RdeReportActionTest {
|
||||
return loadByKey(Cursor.createVKey(RDE_REPORT, "test")).getCursorTime();
|
||||
}
|
||||
|
||||
private static ImmutableMap<String, String> mapifyHeaders(Iterable<HTTPHeader> headers) {
|
||||
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<>();
|
||||
for (HTTPHeader header : headers) {
|
||||
builder.put(Ascii.toUpperCase(header.getName().replace('-', '_')), header.getValue());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static XjcRdeReportReport parseReport(byte[] data) {
|
||||
try {
|
||||
return XjcXmlTransformer.unmarshal(XjcRdeReportReport.class, new ByteArrayInputStream(data));
|
||||
|
||||
@@ -69,6 +69,8 @@ import google.registry.testing.FakeSleeper;
|
||||
import google.registry.testing.GpgSystemCommandExtension;
|
||||
import google.registry.testing.Lazies;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import google.registry.testing.TestOfyOnly;
|
||||
import google.registry.testing.TestSqlOnly;
|
||||
import google.registry.testing.sftp.SftpServerExtension;
|
||||
import google.registry.util.Retrier;
|
||||
import java.io.File;
|
||||
@@ -91,6 +93,7 @@ public class RdeUploadActionTest {
|
||||
|
||||
private static final ByteSource REPORT_XML = RdeTestData.loadBytes("report.xml");
|
||||
private static final ByteSource DEPOSIT_XML = RdeTestData.loadBytes("deposit_full.xml");
|
||||
private static final String JOB_PREFIX = "rde-2010-10-17t00-00-00z";
|
||||
|
||||
private static final BlobId GHOSTRYDE_FILE =
|
||||
BlobId.of("bucket", "tld_2010-10-17_full_S1_R0.xml.ghostryde");
|
||||
@@ -99,11 +102,11 @@ public class RdeUploadActionTest {
|
||||
private static final BlobId REPORT_FILE =
|
||||
BlobId.of("bucket", "tld_2010-10-17_full_S1_R0-report.xml.ghostryde");
|
||||
private static final BlobId GHOSTRYDE_FILE_WITH_PREFIX =
|
||||
BlobId.of("bucket", "job-name/tld_2010-10-17_full_S1_R0.xml.ghostryde");
|
||||
BlobId.of("bucket", JOB_PREFIX + "-job-name/tld_2010-10-17_full_S1_R0.xml.ghostryde");
|
||||
private static final BlobId LENGTH_FILE_WITH_PREFIX =
|
||||
BlobId.of("bucket", "job-name/tld_2010-10-17_full_S1_R0.xml.length");
|
||||
BlobId.of("bucket", JOB_PREFIX + "-job-name/tld_2010-10-17_full_S1_R0.xml.length");
|
||||
private static final BlobId REPORT_FILE_WITH_PREFIX =
|
||||
BlobId.of("bucket", "job-name/tld_2010-10-17_full_S1_R0-report.xml.ghostryde");
|
||||
BlobId.of("bucket", JOB_PREFIX + "-job-name/tld_2010-10-17_full_S1_R0-report.xml.ghostryde");
|
||||
|
||||
private static final BlobId GHOSTRYDE_R1_FILE =
|
||||
BlobId.of("bucket", "tld_2010-10-17_full_S1_R1.xml.ghostryde");
|
||||
@@ -181,7 +184,6 @@ public class RdeUploadActionTest {
|
||||
return jschSpy;
|
||||
}
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
// Force "development" mode so we don't try to really connect to GCS.
|
||||
@@ -194,6 +196,13 @@ public class RdeUploadActionTest {
|
||||
gcsUtils.createFromBytes(LENGTH_R1_FILE, Long.toString(DEPOSIT_XML.size()).getBytes(UTF_8));
|
||||
gcsUtils.createFromBytes(REPORT_FILE, Ghostryde.encode(REPORT_XML.read(), encryptKey));
|
||||
gcsUtils.createFromBytes(REPORT_R1_FILE, Ghostryde.encode(REPORT_XML.read(), encryptKey));
|
||||
gcsUtils.createFromBytes(
|
||||
GHOSTRYDE_FILE_WITH_PREFIX, Ghostryde.encode(DEPOSIT_XML.read(), encryptKey));
|
||||
gcsUtils.createFromBytes(
|
||||
LENGTH_FILE_WITH_PREFIX, Long.toString(DEPOSIT_XML.size()).getBytes(UTF_8));
|
||||
gcsUtils.createFromBytes(
|
||||
REPORT_FILE_WITH_PREFIX, Ghostryde.encode(REPORT_XML.read(), encryptKey));
|
||||
|
||||
tm().transact(
|
||||
() -> {
|
||||
RdeRevision.saveRevision("lol", DateTime.parse("2010-10-17TZ"), FULL, 0);
|
||||
@@ -284,13 +293,36 @@ public class RdeUploadActionTest {
|
||||
assertThat(thrown).hasMessageThat().contains("The crow flies in square circles.");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
@TestSqlOnly
|
||||
void testRunWithLock_cannotGuessPrefix() throws Exception {
|
||||
int port = sftpd.serve("user", "password", folder);
|
||||
URI uploadUrl = URI.create(String.format("sftp://user:password@localhost:%d/", port));
|
||||
DateTime stagingCursor = DateTime.parse("2010-10-18TZ");
|
||||
DateTime uploadCursor = DateTime.parse("2010-10-17TZ");
|
||||
persistResource(Cursor.create(RDE_STAGING, stagingCursor, Registry.get("tld")));
|
||||
gcsUtils.delete(GHOSTRYDE_FILE_WITH_PREFIX);
|
||||
gcsUtils.delete(LENGTH_FILE_WITH_PREFIX);
|
||||
gcsUtils.delete(REPORT_FILE_WITH_PREFIX);
|
||||
RdeUploadAction action = createAction(uploadUrl);
|
||||
NoContentException thrown =
|
||||
assertThrows(NoContentException.class, () -> action.runWithLock(uploadCursor));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("RDE deposit for TLD tld on 2010-10-17T00:00:00.000Z does not exist");
|
||||
cloudTasksHelper.assertNoTasksEnqueued("rde-upload");
|
||||
assertThat(folder.list()).isEmpty();
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testRunWithLock_copiesOnGcs() throws Exception {
|
||||
int port = sftpd.serve("user", "password", folder);
|
||||
URI uploadUrl = URI.create(String.format("sftp://user:password@localhost:%d/", port));
|
||||
DateTime stagingCursor = DateTime.parse("2010-10-18TZ");
|
||||
DateTime uploadCursor = DateTime.parse("2010-10-17TZ");
|
||||
persistResource(Cursor.create(RDE_STAGING, stagingCursor, Registry.get("tld")));
|
||||
gcsUtils.delete(GHOSTRYDE_FILE_WITH_PREFIX);
|
||||
gcsUtils.delete(LENGTH_FILE_WITH_PREFIX);
|
||||
gcsUtils.delete(REPORT_FILE_WITH_PREFIX);
|
||||
createAction(uploadUrl).runWithLock(uploadCursor);
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
|
||||
@@ -306,7 +338,7 @@ public class RdeUploadActionTest {
|
||||
.isEqualTo(Files.toByteArray(new File(folder, sigFilename)));
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
@TestSqlOnly
|
||||
void testRunWithLock_copiesOnGcs_withPrefix() throws Exception {
|
||||
int port = sftpd.serve("user", "password", folder);
|
||||
URI uploadUrl = URI.create(String.format("sftp://user:password@localhost:%d/", port));
|
||||
@@ -314,16 +346,10 @@ public class RdeUploadActionTest {
|
||||
DateTime uploadCursor = DateTime.parse("2010-10-17TZ");
|
||||
persistResource(Cursor.create(RDE_STAGING, stagingCursor, Registry.get("tld")));
|
||||
RdeUploadAction action = createAction(uploadUrl);
|
||||
action.prefix = Optional.of("job-name/");
|
||||
action.prefix = Optional.of(JOB_PREFIX + "-job-name/");
|
||||
gcsUtils.delete(GHOSTRYDE_FILE);
|
||||
gcsUtils.createFromBytes(
|
||||
GHOSTRYDE_FILE_WITH_PREFIX, Ghostryde.encode(DEPOSIT_XML.read(), encryptKey));
|
||||
gcsUtils.delete(LENGTH_FILE);
|
||||
gcsUtils.createFromBytes(
|
||||
LENGTH_FILE_WITH_PREFIX, Long.toString(DEPOSIT_XML.size()).getBytes(UTF_8));
|
||||
gcsUtils.delete(REPORT_FILE);
|
||||
gcsUtils.createFromBytes(
|
||||
REPORT_FILE_WITH_PREFIX, Ghostryde.encode(REPORT_XML.read(), encryptKey));
|
||||
action.runWithLock(uploadCursor);
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
|
||||
@@ -331,9 +357,49 @@ public class RdeUploadActionTest {
|
||||
cloudTasksHelper.assertNoTasksEnqueued("rde-upload");
|
||||
// Assert that both files are written to SFTP and GCS, and that the contents are identical.
|
||||
String rydeFilename = "tld_2010-10-17_full_S1_R0.ryde";
|
||||
String rydeGcsFilename = "job-name/tld_2010-10-17_full_S1_R0.ryde";
|
||||
String rydeGcsFilename = JOB_PREFIX + "-job-name/tld_2010-10-17_full_S1_R0.ryde";
|
||||
String sigFilename = "tld_2010-10-17_full_S1_R0.sig";
|
||||
String sigGcsFilename = "job-name/tld_2010-10-17_full_S1_R0.sig";
|
||||
String sigGcsFilename = JOB_PREFIX + "-job-name/tld_2010-10-17_full_S1_R0.sig";
|
||||
assertThat(folder.list()).asList().containsExactly(rydeFilename, sigFilename);
|
||||
assertThat(gcsUtils.readBytesFrom(BlobId.of("bucket", rydeGcsFilename)))
|
||||
.isEqualTo(Files.toByteArray(new File(folder, rydeFilename)));
|
||||
assertThat(gcsUtils.readBytesFrom(BlobId.of("bucket", sigGcsFilename)))
|
||||
.isEqualTo(Files.toByteArray(new File(folder, sigFilename)));
|
||||
}
|
||||
|
||||
@TestSqlOnly
|
||||
void testRunWithLock_copiesOnGcs_withoutPrefix() throws Exception {
|
||||
int port = sftpd.serve("user", "password", folder);
|
||||
URI uploadUrl = URI.create(String.format("sftp://user:password@localhost:%d/", port));
|
||||
DateTime stagingCursor = DateTime.parse("2010-10-18TZ");
|
||||
DateTime uploadCursor = DateTime.parse("2010-10-17TZ");
|
||||
persistResource(Cursor.create(RDE_STAGING, stagingCursor, Registry.get("tld")));
|
||||
RdeUploadAction action = createAction(uploadUrl);
|
||||
gcsUtils.delete(GHOSTRYDE_FILE);
|
||||
gcsUtils.delete(LENGTH_FILE);
|
||||
gcsUtils.delete(REPORT_FILE);
|
||||
// Add a folder that is alphabetically before the desired folder and fill it will nonsense data.
|
||||
// It should NOT be picked up.
|
||||
BlobId ghostrydeFileWithPrefixBefore =
|
||||
BlobId.of("bucket", JOB_PREFIX + "-job-nama/tld_2010-10-17_full_S1_R0.xml.ghostryde");
|
||||
BlobId lengthFileWithPrefixBefore =
|
||||
BlobId.of("bucket", JOB_PREFIX + "-job-nama/tld_2010-10-17_full_S1_R0.xml.length");
|
||||
BlobId reportFileWithPrefixBefore =
|
||||
BlobId.of(
|
||||
"bucket", JOB_PREFIX + "-job-nama/tld_2010-10-17_full_S1_R0-report.xml.ghostryde");
|
||||
gcsUtils.createFromBytes(ghostrydeFileWithPrefixBefore, "foo".getBytes(UTF_8));
|
||||
gcsUtils.createFromBytes(lengthFileWithPrefixBefore, "bar".getBytes(UTF_8));
|
||||
gcsUtils.createFromBytes(reportFileWithPrefixBefore, "baz".getBytes(UTF_8));
|
||||
action.runWithLock(uploadCursor);
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
|
||||
assertThat(response.getPayload()).isEqualTo("OK tld 2010-10-17T00:00:00.000Z\n");
|
||||
cloudTasksHelper.assertNoTasksEnqueued("rde-upload");
|
||||
// Assert that both files are written to SFTP and GCS, and that the contents are identical.
|
||||
String rydeFilename = "tld_2010-10-17_full_S1_R0.ryde";
|
||||
String rydeGcsFilename = JOB_PREFIX + "-job-name/tld_2010-10-17_full_S1_R0.ryde";
|
||||
String sigFilename = "tld_2010-10-17_full_S1_R0.sig";
|
||||
String sigGcsFilename = JOB_PREFIX + "-job-name/tld_2010-10-17_full_S1_R0.sig";
|
||||
assertThat(folder.list()).asList().containsExactly(rydeFilename, sigFilename);
|
||||
assertThat(gcsUtils.readBytesFrom(BlobId.of("bucket", rydeGcsFilename)))
|
||||
.isEqualTo(Files.toByteArray(new File(folder, rydeFilename)));
|
||||
@@ -349,6 +415,19 @@ public class RdeUploadActionTest {
|
||||
DateTime stagingCursor = DateTime.parse("2010-10-18TZ");
|
||||
DateTime uploadCursor = DateTime.parse("2010-10-17TZ");
|
||||
persistSimpleResource(Cursor.create(RDE_STAGING, stagingCursor, Registry.get("tld")));
|
||||
BlobId ghostrydeR1FileWithPrefix =
|
||||
BlobId.of("bucket", JOB_PREFIX + "-job-name/tld_2010-10-17_full_S1_R1.xml.ghostryde");
|
||||
BlobId lengthR1FileWithPrefix =
|
||||
BlobId.of("bucket", JOB_PREFIX + "-job-name/tld_2010-10-17_full_S1_R1.xml.length");
|
||||
BlobId reportR1FileWithPrefix =
|
||||
BlobId.of(
|
||||
"bucket", JOB_PREFIX + "-job-name/tld_2010-10-17_full_S1_R1-report.xml.ghostryde");
|
||||
gcsUtils.createFromBytes(
|
||||
ghostrydeR1FileWithPrefix, Ghostryde.encode(DEPOSIT_XML.read(), encryptKey));
|
||||
gcsUtils.createFromBytes(
|
||||
lengthR1FileWithPrefix, Long.toString(DEPOSIT_XML.size()).getBytes(UTF_8));
|
||||
gcsUtils.createFromBytes(
|
||||
reportR1FileWithPrefix, Ghostryde.encode(REPORT_XML.read(), encryptKey));
|
||||
createAction(uploadUrl).runWithLock(uploadCursor);
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
|
||||
|
||||
@@ -1429,6 +1429,33 @@ public class DatabaseHelper {
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a DATASTORE_PRIMARY_NO_ASYNC state on the {@link DatabaseMigrationStateSchedule}.
|
||||
*
|
||||
* <p>In order to allow for tests to manipulate the clock how they need, we start the transitions
|
||||
* one millisecond after the clock's current time (in case the clock's current value is
|
||||
* START_OF_TIME). We then advance the clock one second so that we're in the
|
||||
* DATASTORE_PRIMARY_READ_ONLY phase.
|
||||
*
|
||||
* <p>We must use the current time, otherwise the setting of the migration state will fail due to
|
||||
* an invalid transition.
|
||||
*/
|
||||
public static void setMigrationScheduleToDatastorePrimaryNoAsync(FakeClock fakeClock) {
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
ImmutableSortedMap.of(
|
||||
START_OF_TIME,
|
||||
MigrationState.DATASTORE_ONLY,
|
||||
now.plusMillis(1),
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
now.plusMillis(2),
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC)));
|
||||
fakeClock.advanceBy(Duration.standardSeconds(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a DATASTORE_PRIMARY_READ_ONLY state on the {@link DatabaseMigrationStateSchedule}.
|
||||
*
|
||||
@@ -1452,6 +1479,8 @@ public class DatabaseHelper {
|
||||
now.plusMillis(1),
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
now.plusMillis(2),
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
|
||||
now.plusMillis(3),
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY)));
|
||||
fakeClock.advanceBy(Duration.standardSeconds(1));
|
||||
}
|
||||
@@ -1478,8 +1507,10 @@ public class DatabaseHelper {
|
||||
now.plusMillis(1),
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
now.plusMillis(2),
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
|
||||
now.plusMillis(3),
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
|
||||
now.plusMillis(4),
|
||||
MigrationState.SQL_PRIMARY)));
|
||||
fakeClock.advanceBy(Duration.standardSeconds(1));
|
||||
}
|
||||
|
||||
@@ -54,10 +54,12 @@ public class GetDatabaseMigrationStateCommandTest
|
||||
now.plusHours(1),
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
now.plusHours(2),
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
|
||||
now.plusHours(3),
|
||||
MigrationState.SQL_PRIMARY,
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
|
||||
now.plusHours(4),
|
||||
MigrationState.SQL_PRIMARY,
|
||||
now.plusHours(5),
|
||||
MigrationState.SQL_ONLY);
|
||||
jpaTm().transact(() -> DatabaseMigrationStateSchedule.set(transitions));
|
||||
runCommand();
|
||||
|
||||
@@ -64,14 +64,21 @@ public class SetDatabaseMigrationStateCommandTest
|
||||
void testSuccess_fullSchedule() throws Exception {
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
DateTime datastorePrimary = now.plusHours(1);
|
||||
DateTime datastorePrimaryReadOnly = now.plusHours(2);
|
||||
DateTime sqlPrimary = now.plusHours(3);
|
||||
DateTime sqlOnly = now.plusHours(4);
|
||||
DateTime datastorePrimaryNoAsync = now.plusHours(2);
|
||||
DateTime datastorePrimaryReadOnly = now.plusHours(3);
|
||||
DateTime sqlPrimary = now.plusHours(4);
|
||||
DateTime sqlOnly = now.plusHours(5);
|
||||
runCommandForced(
|
||||
String.format(
|
||||
"--migration_schedule=%s=DATASTORE_ONLY,%s=DATASTORE_PRIMARY,"
|
||||
+ "%s=DATASTORE_PRIMARY_READ_ONLY,%s=SQL_PRIMARY,%s=SQL_ONLY",
|
||||
START_OF_TIME, datastorePrimary, datastorePrimaryReadOnly, sqlPrimary, sqlOnly));
|
||||
+ "%s=DATASTORE_PRIMARY_NO_ASYNC,%s=DATASTORE_PRIMARY_READ_ONLY,"
|
||||
+ "%s=SQL_PRIMARY,%s=SQL_ONLY",
|
||||
START_OF_TIME,
|
||||
datastorePrimary,
|
||||
datastorePrimaryNoAsync,
|
||||
datastorePrimaryReadOnly,
|
||||
sqlPrimary,
|
||||
sqlOnly));
|
||||
assertThat(DatabaseMigrationStateSchedule.get().toValueMap())
|
||||
.containsExactlyEntriesIn(
|
||||
ImmutableSortedMap.of(
|
||||
@@ -79,6 +86,8 @@ public class SetDatabaseMigrationStateCommandTest
|
||||
MigrationState.DATASTORE_ONLY,
|
||||
datastorePrimary,
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
datastorePrimaryNoAsync,
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
|
||||
datastorePrimaryReadOnly,
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
|
||||
sqlPrimary,
|
||||
@@ -110,8 +119,9 @@ public class SetDatabaseMigrationStateCommandTest
|
||||
runCommandForced(
|
||||
String.format(
|
||||
"--migration_schedule=%s=DATASTORE_ONLY,%s=DATASTORE_PRIMARY,"
|
||||
+ "%s=DATASTORE_PRIMARY_READ_ONLY,%s=DATASTORE_PRIMARY",
|
||||
START_OF_TIME, now.plusHours(1), now.plusHours(2), now.plusHours(3)));
|
||||
+ "%s=DATASTORE_PRIMARY_NO_ASYNC,%s=DATASTORE_PRIMARY_READ_ONLY,"
|
||||
+ "%s=DATASTORE_PRIMARY",
|
||||
START_OF_TIME, now.plusHours(1), now.plusHours(2), now.plusHours(3), now.plusHours(4)));
|
||||
assertThat(DatabaseMigrationStateSchedule.get().toValueMap())
|
||||
.containsExactlyEntriesIn(
|
||||
ImmutableSortedMap.of(
|
||||
@@ -120,8 +130,10 @@ public class SetDatabaseMigrationStateCommandTest
|
||||
now.plusHours(1),
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
now.plusHours(2),
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
|
||||
now.plusHours(3),
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
|
||||
now.plusHours(4),
|
||||
MigrationState.DATASTORE_PRIMARY));
|
||||
}
|
||||
|
||||
@@ -152,9 +164,12 @@ public class SetDatabaseMigrationStateCommandTest
|
||||
() ->
|
||||
runCommandForced(
|
||||
String.format(
|
||||
"--migration_schedule=%s=DATASTORE_ONLY,"
|
||||
+ "%s=DATASTORE_PRIMARY,%s=DATASTORE_PRIMARY_READ_ONLY",
|
||||
START_OF_TIME, now.minusHours(2), now.minusHours(1)))))
|
||||
"--migration_schedule=%s=DATASTORE_ONLY,%s=DATASTORE_PRIMARY,"
|
||||
+ "%s=DATASTORE_PRIMARY_NO_ASYNC,%s=DATASTORE_PRIMARY_READ_ONLY",
|
||||
START_OF_TIME,
|
||||
now.minusHours(3),
|
||||
now.minusHours(2),
|
||||
now.minusHours(1)))))
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Cannot transition from current state-as-of-now DATASTORE_ONLY "
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<response>
|
||||
<result code="1000">
|
||||
<msg>Command completed successfully</msg>
|
||||
</result>
|
||||
<resData>
|
||||
<domain:creData
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>%DOMAIN%</domain:name>
|
||||
<domain:crDate>1999-04-03T22:00:01.0Z</domain:crDate>
|
||||
<domain:exDate>2001-04-03T22:00:01.0Z</domain:exDate>
|
||||
</domain:creData>
|
||||
</resData>
|
||||
<trID>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
<svTRID>server-trid</svTRID>
|
||||
</trID>
|
||||
</response>
|
||||
</epp>
|
||||
@@ -261,11 +261,11 @@ td.section {
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">generated on</td>
|
||||
<td class="property_value">2022-03-16 18:17:28.401054</td>
|
||||
<td class="property_value">2022-03-30 20:53:20.574091</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">last flyway file</td>
|
||||
<td id="lastFlywayFile" class="property_value">V114__add_allocation_token_indexes.sql</td>
|
||||
<td id="lastFlywayFile" class="property_value">V115__add_renewal_columns_to_billing_recurrence.sql</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -284,7 +284,7 @@ td.section {
|
||||
generated on
|
||||
</text>
|
||||
<text text-anchor="start" x="4055.5" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
2022-03-16 18:17:28.401054
|
||||
2022-03-30 20:53:20.574091
|
||||
</text>
|
||||
<polygon fill="none" stroke="#888888" points="3968,-4 3968,-44 4233,-44 4233,-4 3968,-4" /> <!-- allocationtoken_a08ccbef -->
|
||||
<g id="node1" class="node">
|
||||
|
||||
@@ -261,11 +261,11 @@ td.section {
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">generated on</td>
|
||||
<td class="property_value">2022-03-16 18:17:27.007916</td>
|
||||
<td class="property_value">2022-03-30 20:53:18.15953</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">last flyway file</td>
|
||||
<td id="lastFlywayFile" class="property_value">V114__add_allocation_token_indexes.sql</td>
|
||||
<td id="lastFlywayFile" class="property_value">V115__add_renewal_columns_to_billing_recurrence.sql</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -274,19 +274,19 @@ td.section {
|
||||
<svg viewbox="0.00 0.00 4949.02 4890.50" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="erDiagram" style="overflow: hidden; width: 100%; height: 800px"> <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 4886.5)">
|
||||
<title>SchemaCrawler_Diagram</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-4886.5 4945.02,-4886.5 4945.02,4 -4,4" />
|
||||
<text text-anchor="start" x="4672.52" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<text text-anchor="start" x="4680.52" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
generated by
|
||||
</text>
|
||||
<text text-anchor="start" x="4755.52" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<text text-anchor="start" x="4763.52" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
SchemaCrawler 16.10.1
|
||||
</text>
|
||||
<text text-anchor="start" x="4671.52" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<text text-anchor="start" x="4679.52" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
generated on
|
||||
</text>
|
||||
<text text-anchor="start" x="4755.52" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
2022-03-16 18:17:27.007916
|
||||
<text text-anchor="start" x="4763.52" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
2022-03-30 20:53:18.15953
|
||||
</text>
|
||||
<polygon fill="none" stroke="#888888" points="4668.02,-4 4668.02,-44 4933.02,-44 4933.02,-4 4668.02,-4" /> <!-- allocationtoken_a08ccbef -->
|
||||
<polygon fill="none" stroke="#888888" points="4676.02,-4 4676.02,-44 4933.02,-44 4933.02,-4 4676.02,-4" /> <!-- allocationtoken_a08ccbef -->
|
||||
<g id="node1" class="node">
|
||||
<title>allocationtoken_a08ccbef</title>
|
||||
<polygon fill="#ebcef2" stroke="transparent" points="3100.5,-651.5 3100.5,-670.5 3299.5,-670.5 3299.5,-651.5 3100.5,-651.5" />
|
||||
@@ -558,40 +558,64 @@ td.section {
|
||||
</g> <!-- billingrecurrence_5fa2cb01 -->
|
||||
<g id="node6" class="node">
|
||||
<title>billingrecurrence_5fa2cb01</title>
|
||||
<polygon fill="#ebcef2" stroke="transparent" points="3114.5,-1104.5 3114.5,-1123.5 3291.5,-1123.5 3291.5,-1104.5 3114.5,-1104.5" />
|
||||
<text text-anchor="start" x="3116.5" y="-1111.3" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">
|
||||
<polygon fill="#ebcef2" stroke="transparent" points="3114.5,-1161.5 3114.5,-1180.5 3291.5,-1180.5 3291.5,-1161.5 3114.5,-1161.5" />
|
||||
<text text-anchor="start" x="3116.5" y="-1168.3" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">
|
||||
public.BillingRecurrence
|
||||
</text>
|
||||
<polygon fill="#ebcef2" stroke="transparent" points="3291.5,-1104.5 3291.5,-1123.5 3417.5,-1123.5 3417.5,-1104.5 3291.5,-1104.5" />
|
||||
<text text-anchor="start" x="3378.5" y="-1110.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<polygon fill="#ebcef2" stroke="transparent" points="3291.5,-1161.5 3291.5,-1180.5 3417.5,-1180.5 3417.5,-1161.5 3291.5,-1161.5" />
|
||||
<text text-anchor="start" x="3378.5" y="-1167.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
[table]
|
||||
</text>
|
||||
<text text-anchor="start" x="3116.5" y="-1092.3" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">
|
||||
<text text-anchor="start" x="3116.5" y="-1149.3" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">
|
||||
billing_recurrence_id
|
||||
</text>
|
||||
<text text-anchor="start" x="3285.5" y="-1148.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
</text>
|
||||
<text text-anchor="start" x="3293.5" y="-1148.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
int8 not null
|
||||
</text>
|
||||
<text text-anchor="start" x="3116.5" y="-1129.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
registrar_id
|
||||
</text>
|
||||
<text text-anchor="start" x="3285.5" y="-1129.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
</text>
|
||||
<text text-anchor="start" x="3293.5" y="-1129.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
text not null
|
||||
</text>
|
||||
<text text-anchor="start" x="3116.5" y="-1110.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
domain_history_revision_id
|
||||
</text>
|
||||
<text text-anchor="start" x="3285.5" y="-1110.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
</text>
|
||||
<text text-anchor="start" x="3293.5" y="-1110.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
int8 not null
|
||||
</text>
|
||||
<text text-anchor="start" x="3116.5" y="-1091.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
domain_repo_id
|
||||
</text>
|
||||
<text text-anchor="start" x="3285.5" y="-1091.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
</text>
|
||||
<text text-anchor="start" x="3293.5" y="-1091.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
int8 not null
|
||||
text not null
|
||||
</text>
|
||||
<text text-anchor="start" x="3116.5" y="-1072.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
registrar_id
|
||||
event_time
|
||||
</text>
|
||||
<text text-anchor="start" x="3285.5" y="-1072.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
</text>
|
||||
<text text-anchor="start" x="3293.5" y="-1072.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
text not null
|
||||
timestamptz not null
|
||||
</text>
|
||||
<text text-anchor="start" x="3116.5" y="-1053.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
domain_history_revision_id
|
||||
flags
|
||||
</text>
|
||||
<text text-anchor="start" x="3285.5" y="-1053.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
</text>
|
||||
<text text-anchor="start" x="3293.5" y="-1053.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
int8 not null
|
||||
_text
|
||||
</text>
|
||||
<text text-anchor="start" x="3116.5" y="-1034.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
domain_repo_id
|
||||
reason
|
||||
</text>
|
||||
<text text-anchor="start" x="3285.5" y="-1034.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
</text>
|
||||
@@ -599,31 +623,31 @@ td.section {
|
||||
text not null
|
||||
</text>
|
||||
<text text-anchor="start" x="3116.5" y="-1015.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
event_time
|
||||
domain_name
|
||||
</text>
|
||||
<text text-anchor="start" x="3285.5" y="-1015.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
</text>
|
||||
<text text-anchor="start" x="3293.5" y="-1015.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
timestamptz not null
|
||||
text not null
|
||||
</text>
|
||||
<text text-anchor="start" x="3116.5" y="-996.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
flags
|
||||
recurrence_end_time
|
||||
</text>
|
||||
<text text-anchor="start" x="3285.5" y="-996.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
</text>
|
||||
<text text-anchor="start" x="3293.5" y="-996.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
_text
|
||||
timestamptz
|
||||
</text>
|
||||
<text text-anchor="start" x="3116.5" y="-977.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
reason
|
||||
recurrence_time_of_year
|
||||
</text>
|
||||
<text text-anchor="start" x="3285.5" y="-977.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
</text>
|
||||
<text text-anchor="start" x="3293.5" y="-977.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
text not null
|
||||
text
|
||||
</text>
|
||||
<text text-anchor="start" x="3116.5" y="-958.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
domain_name
|
||||
renewal_price_behavior
|
||||
</text>
|
||||
<text text-anchor="start" x="3285.5" y="-958.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
</text>
|
||||
@@ -631,33 +655,33 @@ td.section {
|
||||
text not null
|
||||
</text>
|
||||
<text text-anchor="start" x="3116.5" y="-939.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
recurrence_end_time
|
||||
renewal_price_currency
|
||||
</text>
|
||||
<text text-anchor="start" x="3285.5" y="-939.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
</text>
|
||||
<text text-anchor="start" x="3293.5" y="-939.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
timestamptz
|
||||
text
|
||||
</text>
|
||||
<text text-anchor="start" x="3116.5" y="-920.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
recurrence_time_of_year
|
||||
renewal_price_amount
|
||||
</text>
|
||||
<text text-anchor="start" x="3285.5" y="-920.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
</text>
|
||||
<text text-anchor="start" x="3293.5" y="-920.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
text
|
||||
numeric(19, 2)
|
||||
</text>
|
||||
<polygon fill="none" stroke="#888888" points="3113,-914 3113,-1125 3418,-1125 3418,-914 3113,-914" />
|
||||
<polygon fill="none" stroke="#888888" points="3113,-913.5 3113,-1181.5 3418,-1181.5 3418,-913.5 3113,-913.5" />
|
||||
</g> <!-- billingevent_a57d1815->billingrecurrence_5fa2cb01 -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>billingevent_a57d1815:w->billingrecurrence_5fa2cb01:e</title>
|
||||
<path fill="none" stroke="black" d="M3871.97,-834.71C3793.31,-872.06 3491.34,-1066.17 3428.33,-1092.53" />
|
||||
<polygon fill="black" stroke="black" points="3879.6,-831.92 3890.54,-832.73 3884.3,-830.21 3889,-828.5 3889,-828.5 3889,-828.5 3884.3,-830.21 3887.46,-824.27 3879.6,-831.92 3879.6,-831.92" />
|
||||
<ellipse fill="none" stroke="black" cx="3875.85" cy="-833.29" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="3420.9,-1100 3418.01,-1090.42 3419.93,-1089.85 3422.82,-1099.42 3420.9,-1100" />
|
||||
<polyline fill="none" stroke="black" points="3418.5,-1095.5 3423.29,-1094.06 " />
|
||||
<polygon fill="black" stroke="black" points="3425.69,-1098.55 3422.8,-1088.98 3424.71,-1088.4 3427.6,-1097.98 3425.69,-1098.55" />
|
||||
<polyline fill="none" stroke="black" points="3423.29,-1094.06 3428.07,-1092.61 " />
|
||||
<text text-anchor="start" x="3470" y="-1072.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<path fill="none" stroke="black" d="M3872.44,-835.57C3794.18,-879.8 3491.62,-1117.68 3428.23,-1149.08" />
|
||||
<polygon fill="black" stroke="black" points="3879.8,-832.43 3890.77,-832.64 3884.4,-830.46 3889,-828.5 3889,-828.5 3889,-828.5 3884.4,-830.46 3887.23,-824.36 3879.8,-832.43 3879.8,-832.43" />
|
||||
<ellipse fill="none" stroke="black" cx="3876.12" cy="-834" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="3421.1,-1156.89 3417.78,-1147.45 3419.67,-1146.79 3422.99,-1156.22 3421.1,-1156.89" />
|
||||
<polyline fill="none" stroke="black" points="3418.5,-1152.5 3423.22,-1150.84 " />
|
||||
<polygon fill="black" stroke="black" points="3425.82,-1155.23 3422.5,-1145.79 3424.39,-1145.13 3427.71,-1154.56 3425.82,-1155.23" />
|
||||
<polyline fill="none" stroke="black" points="3423.22,-1150.84 3427.93,-1149.18 " />
|
||||
<text text-anchor="start" x="3470" y="-1123.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_billing_event_cancellation_matching_billing_recurrence_id
|
||||
</text>
|
||||
</g> <!-- domainhistory_a54cc226 -->
|
||||
@@ -1235,20 +1259,20 @@ td.section {
|
||||
</g> <!-- billingevent_a57d1815->domainhistory_a54cc226 -->
|
||||
<g id="edge28" class="edge">
|
||||
<title>billingevent_a57d1815:w->domainhistory_a54cc226:e</title>
|
||||
<path fill="none" stroke="black" d="M3871.35,-957.82C3846.78,-940.16 3876.08,-877.25 3837,-852.5 3694.56,-762.28 3243.05,-846.56 3079,-885.5 2903.78,-927.09 2810.85,-892.28 2703,-1036.5 2668.91,-1082.09 2729.25,-1281.26 2686.9,-1305.98" />
|
||||
<path fill="none" stroke="black" d="M3871.35,-957.82C3846.78,-940.16 3876.08,-877.25 3837,-852.5 3694.56,-762.28 3240.38,-836.69 3079,-885.5 2896.41,-940.73 2809.71,-934.38 2703,-1092.5 2678.07,-1129.44 2718.1,-1281.84 2686.62,-1305.43" />
|
||||
<polygon fill="black" stroke="black" points="3879.33,-959.94 3887.85,-966.85 3884.17,-961.22 3889,-962.5 3889,-962.5 3889,-962.5 3884.17,-961.22 3890.15,-958.15 3879.33,-959.94 3879.33,-959.94" />
|
||||
<ellipse fill="none" stroke="black" cx="3875.47" cy="-958.91" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="2679.2,-1313.1 2676.74,-1303.41 2678.67,-1302.91 2681.14,-1312.61 2679.2,-1313.1" />
|
||||
<polyline fill="none" stroke="black" points="2677,-1308.5 2681.85,-1307.27 " />
|
||||
<polygon fill="black" stroke="black" points="2684.05,-1311.86 2681.58,-1302.17 2683.52,-1301.68 2685.99,-1311.37 2684.05,-1311.86" />
|
||||
<polyline fill="none" stroke="black" points="2681.85,-1307.27 2686.69,-1306.03 " />
|
||||
<polygon fill="black" stroke="black" points="2679.47,-1312.96 2676.43,-1303.43 2678.34,-1302.82 2681.38,-1312.35 2679.47,-1312.96" />
|
||||
<polyline fill="none" stroke="black" points="2677,-1308.5 2681.76,-1306.98 " />
|
||||
<polygon fill="black" stroke="black" points="2684.24,-1311.44 2681.2,-1301.91 2683.1,-1301.3 2686.14,-1310.83 2684.24,-1311.44" />
|
||||
<polyline fill="none" stroke="black" points="2681.76,-1306.98 2686.53,-1305.46 " />
|
||||
<text text-anchor="start" x="3169" y="-889.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_billing_event_domain_history
|
||||
</text>
|
||||
</g> <!-- billingevent_a57d1815->domainhistory_a54cc226 -->
|
||||
<g id="edge29" class="edge">
|
||||
<title>billingevent_a57d1815:w->domainhistory_a54cc226:e</title>
|
||||
<path fill="none" stroke="black" d="M3870.71,-981.82C3570.79,-992.47 3573.6,-1268.04 3470,-1562.5 3457.19,-1598.92 3475.72,-1879.04 3452,-1909.5 3237.94,-2184.33 2927.98,-1874.53 2703,-2140.5 2677.3,-2170.88 2713.06,-2307.49 2686.73,-2331.8" />
|
||||
<path fill="none" stroke="black" d="M3870.74,-981.82C3578.68,-992.17 3573.17,-1253.62 3470,-1539.5 3456.03,-1578.22 3477.24,-1876.99 3452,-1909.5 3238.38,-2184.68 2927.98,-1874.53 2703,-2140.5 2677.3,-2170.88 2713.06,-2307.49 2686.73,-2331.8" />
|
||||
<polygon fill="black" stroke="black" points="3879,-981.67 3889.08,-986 3884,-981.59 3889,-981.5 3889,-981.5 3889,-981.5 3884,-981.59 3888.92,-977 3879,-981.67 3879,-981.67" />
|
||||
<ellipse fill="none" stroke="black" cx="3875" cy="-981.74" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="2679.71,-2339.82 2676.16,-2330.47 2678.03,-2329.76 2681.58,-2339.11 2679.71,-2339.82" />
|
||||
@@ -1261,22 +1285,22 @@ td.section {
|
||||
</g> <!-- billingevent_a57d1815->domainhistory_a54cc226 -->
|
||||
<g id="edge30" class="edge">
|
||||
<title>billingevent_a57d1815:w->domainhistory_a54cc226:e</title>
|
||||
<path fill="none" stroke="black" d="M3871.1,-959.11C3831.58,-940.14 3889.42,-846.54 3837,-808.5 3734.78,-734.32 2785.1,-791.52 2703,-887.5 2674.68,-920.61 2717.05,-1256.03 2685.49,-1303.04" />
|
||||
<polygon fill="black" stroke="black" points="3879.17,-960.64 3888.16,-966.92 3884.09,-961.57 3889,-962.5 3889,-962.5 3889,-962.5 3884.09,-961.57 3889.84,-958.08 3879.17,-960.64 3879.17,-960.64" />
|
||||
<ellipse fill="none" stroke="black" cx="3875.24" cy="-959.9" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="2680.55,-1312.16 2675.14,-1303.75 2676.82,-1302.67 2682.23,-1311.08 2680.55,-1312.16" />
|
||||
<path fill="none" stroke="black" d="M3871.16,-959.2C3830.74,-940.3 3889.97,-845.31 3837,-806.5 3735.09,-731.83 2784.41,-792.89 2703,-889.5 2675.06,-922.66 2716.91,-1256.31 2685.46,-1303.07" />
|
||||
<polygon fill="black" stroke="black" points="3879.17,-960.68 3888.18,-966.92 3884.08,-961.59 3889,-962.5 3889,-962.5 3889,-962.5 3884.08,-961.59 3889.82,-958.08 3879.17,-960.68 3879.17,-960.68" />
|
||||
<ellipse fill="none" stroke="black" cx="3875.23" cy="-959.95" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="2680.54,-1312.17 2675.14,-1303.75 2676.82,-1302.67 2682.23,-1311.09 2680.54,-1312.17" />
|
||||
<polyline fill="none" stroke="black" points="2677,-1308.5 2681.21,-1305.8 " />
|
||||
<polygon fill="black" stroke="black" points="2684.75,-1309.46 2679.34,-1301.05 2681.02,-1299.97 2686.43,-1308.38 2684.75,-1309.46" />
|
||||
<polyline fill="none" stroke="black" points="2681.21,-1305.8 2685.41,-1303.09 " />
|
||||
<polygon fill="black" stroke="black" points="2684.75,-1309.46 2679.35,-1301.05 2681.03,-1299.97 2686.43,-1308.38 2684.75,-1309.46" />
|
||||
<polyline fill="none" stroke="black" points="2681.21,-1305.8 2685.41,-1303.1 " />
|
||||
<text text-anchor="start" x="3159" y="-803.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_billing_event_recurrence_history
|
||||
</text>
|
||||
</g> <!-- billingevent_a57d1815->domainhistory_a54cc226 -->
|
||||
<g id="edge31" class="edge">
|
||||
<title>billingevent_a57d1815:w->domainhistory_a54cc226:e</title>
|
||||
<path fill="none" stroke="black" d="M3871.29,-737.96C3845.51,-755.37 3875.89,-819.21 3837,-846.5 3770,-893.51 3525.93,-817.75 3470,-877.5 3432.56,-917.5 3489.68,-1823.73 3452,-1863.5 3423.42,-1893.66 3119.71,-1882.19 3079,-1890.5 2905.9,-1925.84 2811.49,-1880.06 2703,-2019.5 2683.27,-2044.86 2707.21,-2286.85 2684.84,-2329.19" />
|
||||
<polygon fill="black" stroke="black" points="3879.3,-735.94 3890.1,-737.86 3884.15,-734.72 3889,-733.5 3889,-733.5 3889,-733.5 3884.15,-734.72 3887.9,-729.14 3879.3,-735.94 3879.3,-735.94" />
|
||||
<ellipse fill="none" stroke="black" cx="3875.42" cy="-736.92" rx="4" ry="4" />
|
||||
<path fill="none" stroke="black" d="M3871.4,-737.92C3845.73,-755.17 3875.62,-818.42 3837,-845.5 3769.97,-892.5 3525.86,-817.66 3470,-877.5 3432.61,-917.55 3489.68,-1823.73 3452,-1863.5 3423.42,-1893.66 3119.71,-1882.19 3079,-1890.5 2905.9,-1925.84 2811.49,-1880.06 2703,-2019.5 2683.27,-2044.86 2707.21,-2286.85 2684.84,-2329.19" />
|
||||
<polygon fill="black" stroke="black" points="3879.3,-735.93 3890.09,-737.86 3884.15,-734.72 3889,-733.5 3889,-733.5 3889,-733.5 3884.15,-734.72 3887.91,-729.14 3879.3,-735.93 3879.3,-735.93" />
|
||||
<ellipse fill="none" stroke="black" cx="3875.42" cy="-736.91" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="2680.91,-2338.77 2674.64,-2330.98 2676.2,-2329.72 2682.47,-2337.52 2680.91,-2338.77" />
|
||||
<polyline fill="none" stroke="black" points="2677,-2335.5 2680.9,-2332.37 " />
|
||||
<polygon fill="black" stroke="black" points="2684.81,-2335.63 2678.54,-2327.84 2680.1,-2326.59 2686.37,-2334.38 2684.81,-2335.63" />
|
||||
@@ -1675,14 +1699,14 @@ td.section {
|
||||
</g> <!-- billingevent_a57d1815->registrar_6e1503e3 -->
|
||||
<g id="edge51" class="edge">
|
||||
<title>billingevent_a57d1815:w->registrar_6e1503e3:e</title>
|
||||
<path fill="none" stroke="black" d="M3871.61,-994.55C3858.04,-981.95 3861.38,-952.77 3837,-940.5 3763.94,-903.72 3529.31,-968.82 3470,-912.5 3424.51,-869.3 3498.83,-814.24 3452,-772.5 3261.21,-602.45 1407.57,-744.75 1152,-742.5 848.9,-739.84 686.12,-527.97 470,-740.5 440.59,-769.42 483.09,-2162.55 449.17,-2284.93" />
|
||||
<path fill="none" stroke="black" d="M3871.61,-994.55C3858.04,-981.95 3861.38,-952.77 3837,-940.5 3763.94,-903.72 3529.29,-968.85 3470,-912.5 3424.21,-868.98 3499.1,-813.61 3452,-771.5 3261.46,-601.15 1407.58,-738.73 1152,-736.5 848.9,-733.85 686.11,-522.96 470,-735.5 440.5,-764.52 483.2,-2162.13 449.19,-2284.91" />
|
||||
<polygon fill="black" stroke="black" points="3879.54,-997.26 3887.54,-1004.76 3884.27,-998.88 3889,-1000.5 3889,-1000.5 3889,-1000.5 3884.27,-998.88 3890.46,-996.24 3879.54,-997.26 3879.54,-997.26" />
|
||||
<ellipse fill="none" stroke="black" cx="3875.75" cy="-995.97" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="448.8,-2295.23 440.24,-2290.06 441.27,-2288.35 449.83,-2293.52 448.8,-2295.23" />
|
||||
<polyline fill="none" stroke="black" points="444,-2293.5 446.59,-2289.22 " />
|
||||
<polygon fill="black" stroke="black" points="451.38,-2290.95 442.82,-2285.78 443.86,-2284.07 452.42,-2289.24 451.38,-2290.95" />
|
||||
<polyline fill="none" stroke="black" points="446.59,-2289.22 449.17,-2284.94 " />
|
||||
<text text-anchor="start" x="1980" y="-714.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<text text-anchor="start" x="1980" y="-709.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_billing_event_registrar_id
|
||||
</text>
|
||||
</g> <!-- billingcancellation_6eedf614 -->
|
||||
@@ -1820,26 +1844,26 @@ td.section {
|
||||
</g> <!-- billingcancellation_6eedf614->billingevent_a57d1815 -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>billingcancellation_6eedf614:w->billingevent_a57d1815:e</title>
|
||||
<path fill="none" stroke="black" d="M752.36,-895.1C744.62,-868.93 752.39,-812.49 765.5,-799.5 793.83,-771.42 1084.61,-799.33 1124.5,-799.5 1156.83,-799.64 3427.57,-798.33 3452,-819.5 3492.09,-854.24 3434.86,-897.75 3470,-937.5 3595.53,-1079.49 3692.75,-1027.09 3880.5,-1053 4062.99,-1078.18 4162.81,-1181.31 4295,-1053 4302.35,-1045.87 4307.11,-1032.71 4304.52,-1025.18" />
|
||||
<path fill="none" stroke="black" d="M752.36,-895.1C744.62,-868.93 752.39,-812.49 765.5,-799.5 793.83,-771.42 1084.61,-799.36 1124.5,-799.5 1156.83,-799.61 3427.6,-794.29 3452,-815.5 3493.36,-851.46 3433.8,-896.35 3470,-937.5 3595.19,-1079.8 3692.75,-1027.09 3880.5,-1053 4062.99,-1078.18 4162.81,-1181.31 4295,-1053 4302.35,-1045.87 4307.11,-1032.71 4304.52,-1025.18" />
|
||||
<polygon fill="black" stroke="black" points="757.79,-901.09 761.16,-911.52 761.14,-904.79 764.5,-908.5 764.5,-908.5 764.5,-908.5 761.14,-904.79 767.84,-905.48 757.79,-901.09 757.79,-901.09" />
|
||||
<ellipse fill="none" stroke="black" cx="755.1" cy="-898.12" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="4294.06,-1024.22 4299.61,-1015.89 4301.27,-1017 4295.72,-1025.32 4294.06,-1024.22" />
|
||||
<polyline fill="none" stroke="black" points="4296,-1019.5 4300.16,-1022.27 " />
|
||||
<polygon fill="black" stroke="black" points="4298.22,-1026.99 4303.77,-1018.67 4305.43,-1019.78 4299.88,-1028.1 4298.22,-1026.99" />
|
||||
<polyline fill="none" stroke="black" points="4300.16,-1022.27 4304.32,-1025.05 " />
|
||||
<text text-anchor="start" x="2354" y="-808.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<text text-anchor="start" x="2354" y="-805.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_billing_cancellation_billing_event_id
|
||||
</text>
|
||||
</g> <!-- billingcancellation_6eedf614->billingrecurrence_5fa2cb01 -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>billingcancellation_6eedf614:w->billingrecurrence_5fa2cb01:e</title>
|
||||
<path fill="none" stroke="black" d="M753.63,-874.92C748.83,-852.14 755.22,-809.66 765.5,-799.5 779.68,-785.48 1105.71,-792.82 1124.5,-799.5 1149.06,-808.23 1145.65,-827.19 1170,-836.5 1391.72,-921.29 2000.25,-870.23 2237,-887.5 2436.54,-902.06 2488.35,-894.68 2685,-931.5 2855.54,-963.43 2928.25,-924.78 3061,-1036.5 3095.83,-1065.81 3066.51,-1104.69 3105,-1129 3222.42,-1203.18 3317.84,-1225.73 3417.5,-1129 3424.85,-1121.87 3429.61,-1108.71 3427.02,-1101.18" />
|
||||
<path fill="none" stroke="black" d="M753.63,-874.92C748.83,-852.14 755.22,-809.66 765.5,-799.5 779.68,-785.48 1105.71,-792.82 1124.5,-799.5 1149.06,-808.23 1145.65,-827.19 1170,-836.5 1391.72,-921.29 2000.25,-870.23 2237,-887.5 2436.54,-902.06 2491.21,-881.76 2685,-931.5 2861.08,-976.69 2930.08,-966.38 3061,-1092.5 3093.93,-1124.22 3066.37,-1161.04 3105,-1185.5 3134.34,-1204.07 3392.58,-1209.68 3417.5,-1185.5 3424.74,-1178.48 3429.43,-1165.51 3426.89,-1158.1" />
|
||||
<polygon fill="black" stroke="black" points="758.52,-881.48 760.89,-892.19 761.51,-885.49 764.5,-889.5 764.5,-889.5 764.5,-889.5 761.51,-885.49 768.11,-886.81 758.52,-881.48 758.52,-881.48" />
|
||||
<ellipse fill="none" stroke="black" cx="756.13" cy="-878.28" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="3416.56,-1100.22 3422.11,-1091.89 3423.77,-1093 3418.22,-1101.32 3416.56,-1100.22" />
|
||||
<polyline fill="none" stroke="black" points="3418.5,-1095.5 3422.66,-1098.27 " />
|
||||
<polygon fill="black" stroke="black" points="3420.72,-1102.99 3426.27,-1094.67 3427.93,-1095.78 3422.38,-1104.1 3420.72,-1102.99" />
|
||||
<polyline fill="none" stroke="black" points="3422.66,-1098.27 3426.82,-1101.05 " />
|
||||
<polygon fill="black" stroke="black" points="3416.56,-1157.21 3422.11,-1148.89 3423.77,-1150 3418.22,-1158.32 3416.56,-1157.21" />
|
||||
<polyline fill="none" stroke="black" points="3418.5,-1152.5 3422.66,-1155.27 " />
|
||||
<polygon fill="black" stroke="black" points="3420.72,-1159.99 3426.27,-1151.67 3427.93,-1152.78 3422.38,-1161.1 3420.72,-1159.99" />
|
||||
<polyline fill="none" stroke="black" points="3422.66,-1155.27 3426.82,-1158.05 " />
|
||||
<text text-anchor="start" x="1932.5" y="-891.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_billing_cancellation_billing_recurrence_id
|
||||
</text>
|
||||
@@ -2353,13 +2377,13 @@ td.section {
|
||||
</g> <!-- domain_6c51cffa->billingevent_a57d1815 -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>domain_6c51cffa:w->billingevent_a57d1815:e</title>
|
||||
<path fill="none" stroke="black" d="M1447.77,-1896.98C1438.07,-1804.62 1443.43,-1378.05 1453,-1368.5 1457.14,-1364.37 1869.79,-1364.44 1874,-1368.5 1894.36,-1388.15 1882.79,-1850.75 1892,-1877.5 1981.42,-2137.15 2010.34,-2252.79 2255,-2377.5 2314.25,-2407.7 3397.45,-2415.54 3452,-2377.5 3877.03,-2081.12 3513.1,-1672.85 3855,-1283.5 4000.67,-1117.61 4177.91,-1240.16 4295,-1053 4300.25,-1044.61 4305.53,-1032.59 4304.13,-1025.46" />
|
||||
<path fill="none" stroke="black" d="M1447.77,-1896.98C1438.07,-1804.62 1443.43,-1378.05 1453,-1368.5 1457.14,-1364.37 1869.79,-1364.44 1874,-1368.5 1894.36,-1388.15 1882.79,-1850.75 1892,-1877.5 1981.42,-2137.15 2010.34,-2252.79 2255,-2377.5 2314.25,-2407.7 3397.21,-2415.19 3452,-2377.5 3864.83,-2093.47 3535.36,-1710.42 3855,-1324.5 4001.57,-1147.53 4180.05,-1251.97 4295,-1053 4299.89,-1044.54 4305.21,-1032.75 4304.08,-1025.65" />
|
||||
<polygon fill="black" stroke="black" points="1449.66,-1904.78 1447.63,-1915.56 1450.83,-1909.64 1452,-1914.5 1452,-1914.5 1452,-1914.5 1450.83,-1909.64 1456.37,-1913.44 1449.66,-1904.78 1449.66,-1904.78" />
|
||||
<ellipse fill="none" stroke="black" cx="1448.72" cy="-1900.89" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="4293.85,-1024.12 4299.76,-1016.06 4301.38,-1017.24 4295.46,-1025.31 4293.85,-1024.12" />
|
||||
<polyline fill="none" stroke="black" points="4296,-1019.5 4300.03,-1022.46 " />
|
||||
<polygon fill="black" stroke="black" points="4297.88,-1027.08 4303.8,-1019.02 4305.41,-1020.2 4299.5,-1028.26 4297.88,-1027.08" />
|
||||
<polyline fill="none" stroke="black" points="4300.03,-1022.46 4304.06,-1025.41 " />
|
||||
<polygon fill="black" stroke="black" points="4293.76,-1024.08 4299.83,-1016.13 4301.42,-1017.34 4295.36,-1025.3 4293.76,-1024.08" />
|
||||
<polyline fill="none" stroke="black" points="4296,-1019.5 4299.98,-1022.53 " />
|
||||
<polygon fill="black" stroke="black" points="4297.74,-1027.11 4303.8,-1019.16 4305.39,-1020.37 4299.33,-1028.33 4297.74,-1027.11" />
|
||||
<polyline fill="none" stroke="black" points="4299.98,-1022.53 4303.95,-1025.56 " />
|
||||
<text text-anchor="start" x="2773.5" y="-2407.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_domain_transfer_billing_event_id
|
||||
</text>
|
||||
@@ -2379,26 +2403,26 @@ td.section {
|
||||
</g> <!-- domain_6c51cffa->billingrecurrence_5fa2cb01 -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>domain_6c51cffa:w->billingrecurrence_5fa2cb01:e</title>
|
||||
<path fill="none" stroke="black" d="M1446.25,-1631.23C1438.3,-1571.65 1443.75,-1377.85 1453,-1368.5 1469.45,-1351.87 1857.21,-1378.78 1874,-1362.5 1905.94,-1331.53 1860.83,-994.25 1892,-962.5 2005.26,-847.15 2093.42,-944.61 2255,-939.5 2446.02,-933.45 2494,-932.9 2685,-939.5 2726.85,-940.95 3028.66,-938.89 3061,-965.5 3119.11,-1013.31 3045.27,-1083.22 3105,-1129 3215.23,-1213.49 3317.84,-1225.73 3417.5,-1129 3424.85,-1121.87 3429.61,-1108.71 3427.02,-1101.18" />
|
||||
<path fill="none" stroke="black" d="M1446.25,-1631.23C1438.3,-1571.65 1443.75,-1377.85 1453,-1368.5 1469.45,-1351.87 1857.21,-1378.78 1874,-1362.5 1905.94,-1331.53 1860.83,-994.25 1892,-962.5 2005.26,-847.15 2093.42,-944.61 2255,-939.5 2446.02,-933.45 2494,-932.9 2685,-939.5 2726.85,-940.95 3029.55,-937.85 3061,-965.5 3135.89,-1031.34 3027.89,-1122.28 3105,-1185.5 3158.7,-1229.53 3367.66,-1233.86 3417.5,-1185.5 3424.74,-1178.48 3429.43,-1165.51 3426.89,-1158.1" />
|
||||
<polygon fill="black" stroke="black" points="1448.84,-1639.01 1447.73,-1649.92 1450.42,-1643.76 1452,-1648.5 1452,-1648.5 1452,-1648.5 1450.42,-1643.76 1456.27,-1647.08 1448.84,-1639.01 1448.84,-1639.01" />
|
||||
<ellipse fill="none" stroke="black" cx="1447.58" cy="-1635.22" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="3416.56,-1100.22 3422.11,-1091.89 3423.77,-1093 3418.22,-1101.32 3416.56,-1100.22" />
|
||||
<polyline fill="none" stroke="black" points="3418.5,-1095.5 3422.66,-1098.27 " />
|
||||
<polygon fill="black" stroke="black" points="3420.72,-1102.99 3426.27,-1094.67 3427.93,-1095.78 3422.38,-1104.1 3420.72,-1102.99" />
|
||||
<polyline fill="none" stroke="black" points="3422.66,-1098.27 3426.82,-1101.05 " />
|
||||
<polygon fill="black" stroke="black" points="3416.56,-1157.21 3422.11,-1148.89 3423.77,-1150 3418.22,-1158.32 3416.56,-1157.21" />
|
||||
<polyline fill="none" stroke="black" points="3418.5,-1152.5 3422.66,-1155.27 " />
|
||||
<polygon fill="black" stroke="black" points="3420.72,-1159.99 3426.27,-1151.67 3427.93,-1152.78 3422.38,-1161.1 3420.72,-1159.99" />
|
||||
<polyline fill="none" stroke="black" points="3422.66,-1155.27 3426.82,-1158.05 " />
|
||||
<text text-anchor="start" x="2372" y="-943.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_domain_billing_recurrence_id
|
||||
</text>
|
||||
</g> <!-- domain_6c51cffa->billingrecurrence_5fa2cb01 -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>domain_6c51cffa:w->billingrecurrence_5fa2cb01:e</title>
|
||||
<path fill="none" stroke="black" d="M1447.83,-1877.7C1438.61,-1786.75 1443.86,-1377.76 1453,-1368.5 1461.22,-1360.18 1864.62,-1369.48 1874,-1362.5 1895.32,-1346.64 1877.51,-1327.76 1892,-1305.5 2009.34,-1125.26 2053.81,-1061.52 2255,-985.5 2344.39,-951.73 2590.74,-969.84 2685,-985.5 2879.59,-1017.83 2910.41,-1096.67 3105,-1129 3242.01,-1151.76 3317.84,-1225.73 3417.5,-1129 3424.85,-1121.87 3429.61,-1108.71 3427.02,-1101.18" />
|
||||
<path fill="none" stroke="black" d="M1447.83,-1877.7C1438.61,-1786.75 1443.86,-1377.76 1453,-1368.5 1461.22,-1360.18 1864.62,-1369.48 1874,-1362.5 1895.32,-1346.64 1877.51,-1327.76 1892,-1305.5 2009.34,-1125.26 2053.81,-1061.52 2255,-985.5 2344.39,-951.73 2591.79,-964.44 2685,-985.5 2886.67,-1031.06 2903.33,-1139.94 3105,-1185.5 3240.47,-1216.11 3317.81,-1282.21 3417.5,-1185.5 3424.74,-1178.48 3429.43,-1165.51 3426.89,-1158.1" />
|
||||
<polygon fill="black" stroke="black" points="1449.72,-1885.76 1447.62,-1896.53 1450.86,-1890.63 1452,-1895.5 1452,-1895.5 1452,-1895.5 1450.86,-1890.63 1456.38,-1894.47 1449.72,-1885.76 1449.72,-1885.76" />
|
||||
<ellipse fill="none" stroke="black" cx="1448.81" cy="-1881.87" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="3416.56,-1100.22 3422.11,-1091.89 3423.77,-1093 3418.22,-1101.32 3416.56,-1100.22" />
|
||||
<polyline fill="none" stroke="black" points="3418.5,-1095.5 3422.66,-1098.27 " />
|
||||
<polygon fill="black" stroke="black" points="3420.72,-1102.99 3426.27,-1094.67 3427.93,-1095.78 3422.38,-1104.1 3420.72,-1102.99" />
|
||||
<polyline fill="none" stroke="black" points="3422.66,-1098.27 3426.82,-1101.05 " />
|
||||
<polygon fill="black" stroke="black" points="3416.56,-1157.21 3422.11,-1148.89 3423.77,-1150 3418.22,-1158.32 3416.56,-1157.21" />
|
||||
<polyline fill="none" stroke="black" points="3418.5,-1152.5 3422.66,-1155.27 " />
|
||||
<polygon fill="black" stroke="black" points="3420.72,-1159.99 3426.27,-1151.67 3427.93,-1152.78 3422.38,-1161.1 3420.72,-1159.99" />
|
||||
<polyline fill="none" stroke="black" points="3422.66,-1155.27 3426.82,-1158.05 " />
|
||||
<text text-anchor="start" x="2345.5" y="-989.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_domain_transfer_billing_recurrence_id
|
||||
</text>
|
||||
@@ -2881,53 +2905,53 @@ td.section {
|
||||
</g> <!-- domain_6c51cffa->contact_8de8cb16 -->
|
||||
<g id="edge13" class="edge">
|
||||
<title>domain_6c51cffa:w->contact_8de8cb16:e</title>
|
||||
<path fill="none" stroke="black" d="M1436.91,-2058.53C1416.8,-2097.51 1454.86,-2238.31 1426,-2269.5 1331.6,-2371.52 1257.66,-2333.49 1121.82,-2331.57" />
|
||||
<polygon fill="black" stroke="black" points="1443.67,-2054.04 1454.49,-2052.25 1447.84,-2051.27 1452,-2048.5 1452,-2048.5 1452,-2048.5 1447.84,-2051.27 1449.51,-2044.75 1443.67,-2054.04 1443.67,-2054.04" />
|
||||
<ellipse fill="none" stroke="black" cx="1440.34" cy="-2056.25" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="1112.46,-2336.51 1112.54,-2326.51 1114.54,-2326.52 1114.46,-2336.52 1112.46,-2336.51" />
|
||||
<polyline fill="none" stroke="black" points="1111.5,-2331.5 1116.5,-2331.54 " />
|
||||
<polygon fill="black" stroke="black" points="1117.46,-2336.54 1117.54,-2326.54 1119.54,-2326.56 1119.46,-2336.56 1117.46,-2336.54" />
|
||||
<polyline fill="none" stroke="black" points="1116.5,-2331.54 1121.5,-2331.57 " />
|
||||
<text text-anchor="start" x="1218" y="-2344.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<path fill="none" stroke="black" d="M1437.49,-2059.39C1421.3,-2096.02 1449.83,-2213.94 1426,-2241.5 1333.13,-2348.9 1260.37,-2332.36 1121.61,-2331.53" />
|
||||
<polygon fill="black" stroke="black" points="1444,-2054.5 1454.7,-2052.1 1448,-2051.5 1452,-2048.5 1452,-2048.5 1452,-2048.5 1448,-2051.5 1449.3,-2044.9 1444,-2054.5 1444,-2054.5" />
|
||||
<ellipse fill="none" stroke="black" cx="1440.8" cy="-2056.91" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="1112.48,-2336.5 1112.52,-2326.5 1114.52,-2326.51 1114.48,-2336.51 1112.48,-2336.5" />
|
||||
<polyline fill="none" stroke="black" points="1111.5,-2331.5 1116.5,-2331.52 " />
|
||||
<polygon fill="black" stroke="black" points="1117.48,-2336.52 1117.52,-2326.52 1119.52,-2326.52 1119.48,-2336.52 1117.48,-2336.52" />
|
||||
<polyline fill="none" stroke="black" points="1116.5,-2331.52 1121.5,-2331.53 " />
|
||||
<text text-anchor="start" x="1218" y="-2337.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_domain_admin_contact
|
||||
</text>
|
||||
</g> <!-- domain_6c51cffa->contact_8de8cb16 -->
|
||||
<g id="edge14" class="edge">
|
||||
<title>domain_6c51cffa:w->contact_8de8cb16:e</title>
|
||||
<path fill="none" stroke="black" d="M1438.78,-2042.24C1428.56,-2074.05 1443.62,-2155.85 1426,-2175.5 1346.85,-2263.77 1266.83,-2182.08 1170,-2250.5 1136.85,-2273.92 1152.53,-2322.2 1121.55,-2330.33" />
|
||||
<polygon fill="black" stroke="black" points="1444.8,-2036.44 1455.12,-2032.74 1448.4,-2032.97 1452,-2029.5 1452,-2029.5 1452,-2029.5 1448.4,-2032.97 1448.88,-2026.26 1444.8,-2036.44 1444.8,-2036.44" />
|
||||
<ellipse fill="none" stroke="black" cx="1441.92" cy="-2039.21" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="1113.07,-2336.35 1111.91,-2326.42 1113.9,-2326.19 1115.06,-2336.12 1113.07,-2336.35" />
|
||||
<polyline fill="none" stroke="black" points="1111.5,-2331.5 1116.47,-2330.92 " />
|
||||
<polygon fill="black" stroke="black" points="1118.04,-2335.77 1116.88,-2325.84 1118.87,-2325.61 1120.03,-2335.54 1118.04,-2335.77" />
|
||||
<polyline fill="none" stroke="black" points="1116.47,-2330.92 1121.43,-2330.34 " />
|
||||
<text text-anchor="start" x="1219.5" y="-2254.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<path fill="none" stroke="black" d="M1436.82,-2039.75C1433.13,-2052.92 1439.64,-2075.83 1426,-2090.5 1338.85,-2184.27 1261.26,-2132.73 1170,-2222.5 1133.48,-2258.42 1161.95,-2322.02 1121.56,-2330.55" />
|
||||
<polygon fill="black" stroke="black" points="1443.71,-2035.1 1454.52,-2033.23 1447.86,-2032.3 1452,-2029.5 1452,-2029.5 1452,-2029.5 1447.86,-2032.3 1449.48,-2025.77 1443.71,-2035.1 1443.71,-2035.1" />
|
||||
<ellipse fill="none" stroke="black" cx="1440.4" cy="-2037.33" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="1112.97,-2336.38 1112.02,-2326.43 1114.01,-2326.24 1114.96,-2336.19 1112.97,-2336.38" />
|
||||
<polyline fill="none" stroke="black" points="1111.5,-2331.5 1116.48,-2331.03 " />
|
||||
<polygon fill="black" stroke="black" points="1117.95,-2335.91 1117,-2325.96 1118.99,-2325.77 1119.94,-2335.72 1117.95,-2335.91" />
|
||||
<polyline fill="none" stroke="black" points="1116.48,-2331.03 1121.46,-2330.55 " />
|
||||
<text text-anchor="start" x="1219.5" y="-2226.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_domain_billing_contact
|
||||
</text>
|
||||
</g> <!-- domain_6c51cffa->contact_8de8cb16 -->
|
||||
<g id="edge15" class="edge">
|
||||
<title>domain_6c51cffa:w->contact_8de8cb16:e</title>
|
||||
<path fill="none" stroke="black" d="M1435.21,-2017.96C1432.76,-2020.09 1429.99,-2022.4 1426,-2024.5 1312.75,-2084.18 1253.31,-2059.31 1170,-2156.5 1119.02,-2215.97 1186.86,-2321.47 1121.62,-2330.83" />
|
||||
<polygon fill="black" stroke="black" points="1442.86,-2014.56 1453.83,-2014.61 1447.43,-2012.53 1452,-2010.5 1452,-2010.5 1452,-2010.5 1447.43,-2012.53 1450.17,-2006.39 1442.86,-2014.56 1442.86,-2014.56" />
|
||||
<ellipse fill="none" stroke="black" cx="1439.2" cy="-2016.18" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="1112.83,-2336.42 1112.17,-2326.44 1114.16,-2326.31 1114.82,-2336.29 1112.83,-2336.42" />
|
||||
<polyline fill="none" stroke="black" points="1111.5,-2331.5 1116.49,-2331.17 " />
|
||||
<polygon fill="black" stroke="black" points="1117.82,-2336.09 1117.16,-2326.12 1119.15,-2325.98 1119.81,-2335.96 1117.82,-2336.09" />
|
||||
<polyline fill="none" stroke="black" points="1116.49,-2331.17 1121.48,-2330.84 " />
|
||||
<text text-anchor="start" x="1209" y="-2160.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<path fill="none" stroke="black" d="M1438.06,-2022.15C1435.66,-2028.05 1433.31,-2034.74 1426,-2038.5 1324,-2091 1254.81,-1994.24 1170,-2071.5 1085.09,-2148.85 1221.23,-2320.68 1121.54,-2331.01" />
|
||||
<polygon fill="black" stroke="black" points="1444.33,-2016.91 1454.89,-2013.95 1448.16,-2013.71 1452,-2010.5 1452,-2010.5 1452,-2010.5 1448.16,-2013.71 1449.11,-2007.05 1444.33,-2016.91 1444.33,-2016.91" />
|
||||
<ellipse fill="none" stroke="black" cx="1441.26" cy="-2019.48" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="1112.74,-2336.45 1112.26,-2326.46 1114.25,-2326.36 1114.74,-2336.35 1112.74,-2336.45" />
|
||||
<polyline fill="none" stroke="black" points="1111.5,-2331.5 1116.49,-2331.26 " />
|
||||
<polygon fill="black" stroke="black" points="1117.74,-2336.2 1117.25,-2326.21 1119.25,-2326.12 1119.73,-2336.11 1117.74,-2336.2" />
|
||||
<polyline fill="none" stroke="black" points="1116.49,-2331.26 1121.49,-2331.01 " />
|
||||
<text text-anchor="start" x="1209" y="-2075.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_domain_registrant_contact
|
||||
</text>
|
||||
</g> <!-- domain_6c51cffa->contact_8de8cb16 -->
|
||||
<g id="edge16" class="edge">
|
||||
<title>domain_6c51cffa:w->contact_8de8cb16:e</title>
|
||||
<path fill="none" stroke="black" d="M1433.71,-1991.01C1320.23,-1985.02 1259.53,-1926.75 1170,-2005.5 1062.17,-2100.35 1249.16,-2320.41 1121.67,-2331.1" />
|
||||
<polygon fill="black" stroke="black" points="1442,-1991.23 1451.88,-1996 1447,-1991.37 1452,-1991.5 1452,-1991.5 1452,-1991.5 1447,-1991.37 1452.12,-1987 1442,-1991.23 1442,-1991.23" />
|
||||
<ellipse fill="none" stroke="black" cx="1438" cy="-1991.13" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="1112.7,-2336.46 1112.3,-2326.46 1114.3,-2326.38 1114.7,-2336.38 1112.7,-2336.46" />
|
||||
<path fill="none" stroke="black" d="M1433.99,-1991.11C1319.81,-1986.26 1258.29,-1938.53 1170,-2019.5 1068.66,-2112.44 1243.3,-2320.58 1121.62,-2331.09" />
|
||||
<polygon fill="black" stroke="black" points="1442,-1991.29 1451.9,-1996 1447,-1991.39 1452,-1991.5 1452,-1991.5 1452,-1991.5 1447,-1991.39 1452.1,-1987 1442,-1991.29 1442,-1991.29" />
|
||||
<ellipse fill="none" stroke="black" cx="1438" cy="-1991.2" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="1112.7,-2336.46 1112.3,-2326.46 1114.29,-2326.38 1114.7,-2336.37 1112.7,-2336.46" />
|
||||
<polyline fill="none" stroke="black" points="1111.5,-2331.5 1116.5,-2331.3 " />
|
||||
<polygon fill="black" stroke="black" points="1117.69,-2336.26 1117.3,-2326.27 1119.3,-2326.19 1119.69,-2336.18 1117.69,-2336.26" />
|
||||
<polyline fill="none" stroke="black" points="1116.5,-2331.3 1121.49,-2331.1 " />
|
||||
<text text-anchor="start" x="1224" y="-2009.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<polygon fill="black" stroke="black" points="1117.7,-2336.25 1117.29,-2326.26 1119.29,-2326.18 1119.7,-2336.17 1117.7,-2336.25" />
|
||||
<polyline fill="none" stroke="black" points="1116.5,-2331.3 1121.49,-2331.09 " />
|
||||
<text text-anchor="start" x="1224" y="-2023.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_domain_tech_contact
|
||||
</text>
|
||||
</g> <!-- domain_6c51cffa->registrar_6e1503e3 -->
|
||||
@@ -2959,9 +2983,9 @@ td.section {
|
||||
</g> <!-- domain_6c51cffa->registrar_6e1503e3 -->
|
||||
<g id="edge61" class="edge">
|
||||
<title>domain_6c51cffa:w->registrar_6e1503e3:e</title>
|
||||
<path fill="none" stroke="black" d="M1434.49,-2357.19C1432.17,-2358.1 1429.51,-2358.94 1426,-2359.5 1305.56,-2378.57 1273.9,-2370.39 1152,-2373.5 964.06,-2378.3 916.36,-2388.95 729,-2373.5 612.71,-2363.91 568.78,-2392.62 470,-2330.5 456.97,-2322.31 460.43,-2304.97 453.75,-2297.29" />
|
||||
<polygon fill="black" stroke="black" points="1442.34,-2355.09 1453.16,-2356.85 1447.17,-2353.79 1452,-2352.5 1452,-2352.5 1452,-2352.5 1447.17,-2353.79 1450.84,-2348.15 1442.34,-2355.09 1442.34,-2355.09" />
|
||||
<ellipse fill="none" stroke="black" cx="1438.48" cy="-2356.12" rx="4" ry="4" />
|
||||
<path fill="none" stroke="black" d="M1433.85,-2352.6C1315.05,-2354.01 1279.32,-2369.05 1152,-2373.5 964.11,-2380.07 916.36,-2388.95 729,-2373.5 612.71,-2363.91 568.78,-2392.62 470,-2330.5 456.97,-2322.31 460.43,-2304.97 453.75,-2297.29" />
|
||||
<polygon fill="black" stroke="black" points="1442,-2352.56 1452.03,-2357 1447,-2352.53 1452,-2352.5 1452,-2352.5 1452,-2352.5 1447,-2352.53 1451.97,-2348 1442,-2352.56 1442,-2352.56" />
|
||||
<ellipse fill="none" stroke="black" cx="1438" cy="-2352.58" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="443.12,-2298.52 446.75,-2289.2 448.61,-2289.93 444.98,-2299.25 443.12,-2298.52" />
|
||||
<polyline fill="none" stroke="black" points="444,-2293.5 448.66,-2295.31 " />
|
||||
<polygon fill="black" stroke="black" points="447.78,-2300.34 451.4,-2291.02 453.27,-2291.74 449.64,-2301.06 447.78,-2300.34" />
|
||||
@@ -3440,9 +3464,9 @@ td.section {
|
||||
</g> <!-- graceperiod_cd3b2e8f->domain_6c51cffa -->
|
||||
<g id="edge23" class="edge">
|
||||
<title>graceperiod_cd3b2e8f:w->domain_6c51cffa:e</title>
|
||||
<path fill="none" stroke="black" d="M4559.94,-1763.61C4556.06,-1770.43 4553.72,-1779.71 4545,-1786.5 4104.83,-2129.53 3988.27,-2250.12 3452,-2404.5 3326.23,-2440.71 2928.48,-2447.46 1876.07,-2447.5" />
|
||||
<polygon fill="black" stroke="black" points="4566.87,-1759.55 4577.77,-1758.38 4571.19,-1757.03 4575.5,-1754.5 4575.5,-1754.5 4575.5,-1754.5 4571.19,-1757.03 4573.23,-1750.62 4566.87,-1759.55 4566.87,-1759.55" />
|
||||
<ellipse fill="none" stroke="black" cx="4563.42" cy="-1761.58" rx="4" ry="4" />
|
||||
<path fill="none" stroke="black" d="M4559.93,-1763.92C4556.08,-1771.21 4554.03,-1781.25 4545,-1788.5 4110.16,-2137.57 3987.96,-2250.61 3452,-2404.5 3325.9,-2440.71 2927.48,-2447.46 1876.06,-2447.5" />
|
||||
<polygon fill="black" stroke="black" points="4566.95,-1759.68 4577.83,-1758.35 4571.22,-1757.09 4575.5,-1754.5 4575.5,-1754.5 4575.5,-1754.5 4571.22,-1757.09 4573.17,-1750.65 4566.95,-1759.68 4566.95,-1759.68" />
|
||||
<ellipse fill="none" stroke="black" cx="4563.52" cy="-1761.75" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="1867,-2452.5 1867,-2442.5 1869,-2442.5 1869,-2452.5 1867,-2452.5" />
|
||||
<polyline fill="none" stroke="black" points="1866,-2447.5 1871,-2447.5 " />
|
||||
<polygon fill="black" stroke="black" points="1872,-2452.5 1872,-2442.5 1874,-2442.5 1874,-2452.5 1872,-2452.5" />
|
||||
@@ -3453,20 +3477,20 @@ td.section {
|
||||
</g> <!-- graceperiod_cd3b2e8f->billingrecurrence_5fa2cb01 -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>graceperiod_cd3b2e8f:w->billingrecurrence_5fa2cb01:e</title>
|
||||
<path fill="none" stroke="black" d="M4557.23,-1792.28C3979.41,-1778.36 4012.26,-1103.48 3428.88,-1095.57" />
|
||||
<polygon fill="black" stroke="black" points="4565.5,-1792.38 4575.45,-1797 4570.5,-1792.44 4575.5,-1792.5 4575.5,-1792.5 4575.5,-1792.5 4570.5,-1792.44 4575.55,-1788 4565.5,-1792.38 4565.5,-1792.38" />
|
||||
<ellipse fill="none" stroke="black" cx="4561.5" cy="-1792.33" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="3419.47,-1100.51 3419.53,-1090.51 3421.53,-1090.52 3421.47,-1100.52 3419.47,-1100.51" />
|
||||
<polyline fill="none" stroke="black" points="3418.5,-1095.5 3423.5,-1095.53 " />
|
||||
<polygon fill="black" stroke="black" points="3424.47,-1100.54 3424.53,-1090.54 3426.53,-1090.55 3426.47,-1100.55 3424.47,-1100.54" />
|
||||
<polyline fill="none" stroke="black" points="3423.5,-1095.53 3428.5,-1095.57 " />
|
||||
<text text-anchor="start" x="3977.5" y="-1745.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<path fill="none" stroke="black" d="M4557.19,-1792.29C3991.5,-1779.21 3999.86,-1159.82 3428.66,-1152.56" />
|
||||
<polygon fill="black" stroke="black" points="4565.5,-1792.39 4575.45,-1797 4570.5,-1792.44 4575.5,-1792.5 4575.5,-1792.5 4575.5,-1792.5 4570.5,-1792.44 4575.55,-1788 4565.5,-1792.39 4565.5,-1792.39" />
|
||||
<ellipse fill="none" stroke="black" cx="4561.5" cy="-1792.34" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="3419.47,-1157.51 3419.53,-1147.51 3421.53,-1147.52 3421.47,-1157.52 3419.47,-1157.51" />
|
||||
<polyline fill="none" stroke="black" points="3418.5,-1152.5 3423.5,-1152.53 " />
|
||||
<polygon fill="black" stroke="black" points="3424.47,-1157.54 3424.53,-1147.54 3426.53,-1147.55 3426.47,-1157.55 3424.47,-1157.54" />
|
||||
<polyline fill="none" stroke="black" points="3423.5,-1152.53 3428.5,-1152.56 " />
|
||||
<text text-anchor="start" x="3977.5" y="-1749.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_grace_period_billing_recurrence_id
|
||||
</text>
|
||||
</g> <!-- graceperiod_cd3b2e8f->registrar_6e1503e3 -->
|
||||
<g id="edge65" class="edge">
|
||||
<title>graceperiod_cd3b2e8f:w->registrar_6e1503e3:e</title>
|
||||
<path fill="none" stroke="black" d="M4562.37,-1761.11C4494.36,-1645.72 4352,-762.28 4329,-710.5 4254.48,-542.72 4276.58,-371.5 4093,-371.5 4093,-371.5 4093,-371.5 939.5,-371.5 698.57,-371.5 592.12,-434.81 470,-642.5 447.75,-680.34 486.28,-2157.92 449.43,-2284.83" />
|
||||
<path fill="none" stroke="black" d="M4562.37,-1761.11C4494.36,-1645.72 4352,-762.28 4329,-710.5 4254.48,-542.72 4276.58,-371.5 4093,-371.5 4093,-371.5 4093,-371.5 939.5,-371.5 699.67,-371.5 592.38,-431.24 470,-637.5 447.53,-675.37 486.39,-2157.5 449.45,-2284.8" />
|
||||
<polygon fill="black" stroke="black" points="4568.23,-1766.64 4572.41,-1776.77 4571.86,-1770.07 4575.5,-1773.5 4575.5,-1773.5 4575.5,-1773.5 4571.86,-1770.07 4578.59,-1770.23 4568.23,-1766.64 4568.23,-1766.64" />
|
||||
<ellipse fill="none" stroke="black" cx="4565.32" cy="-1763.89" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="448.77,-2295.31 440.29,-2290 441.35,-2288.3 449.83,-2293.61 448.77,-2295.31" />
|
||||
@@ -3479,22 +3503,22 @@ td.section {
|
||||
</g> <!-- billingrecurrence_5fa2cb01->domainhistory_a54cc226 -->
|
||||
<g id="edge32" class="edge">
|
||||
<title>billingrecurrence_5fa2cb01:w->domainhistory_a54cc226:e</title>
|
||||
<path fill="none" stroke="black" d="M3095.23,-1038.63C2907.08,-1041.28 2833.35,-1086.69 2703,-1232.5 2682,-1256 2705.64,-1297.24 2686.9,-1306.58" />
|
||||
<polygon fill="black" stroke="black" points="3103.5,-1038.57 3113.53,-1043 3108.5,-1038.53 3113.5,-1038.5 3113.5,-1038.5 3113.5,-1038.5 3108.5,-1038.53 3113.47,-1034 3103.5,-1038.57 3103.5,-1038.57" />
|
||||
<ellipse fill="none" stroke="black" cx="3099.5" cy="-1038.6" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="2678.93,-1313.22 2677.03,-1303.4 2678.99,-1303.02 2680.9,-1312.84 2678.93,-1313.22" />
|
||||
<polyline fill="none" stroke="black" points="2677,-1308.5 2681.91,-1307.55 " />
|
||||
<polygon fill="black" stroke="black" points="2683.84,-1312.27 2681.94,-1302.45 2683.9,-1302.07 2685.8,-1311.89 2683.84,-1312.27" />
|
||||
<polyline fill="none" stroke="black" points="2681.91,-1307.55 2686.82,-1306.6 " />
|
||||
<path fill="none" stroke="black" d="M3095.24,-1095.51C2915.84,-1095.74 2835.11,-1101.38 2703,-1232.5 2680.63,-1254.7 2705.32,-1296.93 2686.85,-1306.53" />
|
||||
<polygon fill="black" stroke="black" points="3103.5,-1095.51 3113.5,-1100 3108.5,-1095.5 3113.5,-1095.5 3113.5,-1095.5 3113.5,-1095.5 3108.5,-1095.5 3113.5,-1091 3103.5,-1095.51 3103.5,-1095.51" />
|
||||
<ellipse fill="none" stroke="black" cx="3099.5" cy="-1095.51" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="2678.96,-1313.21 2677,-1303.4 2678.96,-1303.01 2680.92,-1312.81 2678.96,-1313.21" />
|
||||
<polyline fill="none" stroke="black" points="2677,-1308.5 2681.9,-1307.52 " />
|
||||
<polygon fill="black" stroke="black" points="2683.86,-1312.22 2681.9,-1302.42 2683.86,-1302.03 2685.83,-1311.83 2683.86,-1312.22" />
|
||||
<polyline fill="none" stroke="black" points="2681.9,-1307.52 2686.81,-1306.54 " />
|
||||
<text text-anchor="start" x="2769.5" y="-1236.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_billing_recurrence_domain_history
|
||||
</text>
|
||||
</g> <!-- billingrecurrence_5fa2cb01->domainhistory_a54cc226 -->
|
||||
<g id="edge33" class="edge">
|
||||
<title>billingrecurrence_5fa2cb01:w->domainhistory_a54cc226:e</title>
|
||||
<path fill="none" stroke="black" d="M3102.02,-1071.66C3082.53,-1124.69 3079.92,-1309.46 3061,-1327.5 2944.63,-1438.44 2809.84,-1259.36 2703,-1379.5 2669.52,-1417.15 2723.93,-2243 2684.35,-2328.38" />
|
||||
<polygon fill="black" stroke="black" points="3107.2,-1065.27 3116.99,-1060.33 3110.35,-1061.38 3113.5,-1057.5 3113.5,-1057.5 3113.5,-1057.5 3110.35,-1061.38 3110.01,-1054.67 3107.2,-1065.27 3107.2,-1065.27" />
|
||||
<ellipse fill="none" stroke="black" cx="3104.68" cy="-1068.37" rx="4" ry="4" />
|
||||
<path fill="none" stroke="black" d="M3095.6,-1117.76C3064.47,-1132.06 3090.72,-1190.68 3079,-1239.5 3069.68,-1278.32 3090.03,-1300.1 3061,-1327.5 2944.09,-1437.87 2809.84,-1259.36 2703,-1379.5 2669.52,-1417.15 2723.93,-2243 2684.35,-2328.38" />
|
||||
<polygon fill="black" stroke="black" points="3103.66,-1116.29 3114.31,-1118.93 3108.58,-1115.4 3113.5,-1114.5 3113.5,-1114.5 3113.5,-1114.5 3108.58,-1115.4 3112.69,-1110.07 3103.66,-1116.29 3103.66,-1116.29" />
|
||||
<ellipse fill="none" stroke="black" cx="3099.73" cy="-1117.01" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="2681.2,-2338.4 2674.24,-2331.21 2675.68,-2329.82 2682.63,-2337.01 2681.2,-2338.4" />
|
||||
<polyline fill="none" stroke="black" points="2677,-2335.5 2680.59,-2332.02 " />
|
||||
<polygon fill="black" stroke="black" points="2684.79,-2334.92 2677.83,-2327.73 2679.27,-2326.34 2686.23,-2333.53 2684.79,-2334.92" />
|
||||
@@ -3505,14 +3529,14 @@ td.section {
|
||||
</g> <!-- billingrecurrence_5fa2cb01->registrar_6e1503e3 -->
|
||||
<g id="edge52" class="edge">
|
||||
<title>billingrecurrence_5fa2cb01:w->registrar_6e1503e3:e</title>
|
||||
<path fill="none" stroke="black" d="M3095.63,-1073.04C3058.72,-1054.83 3108.75,-969.53 3061,-931.5 2929.31,-826.61 2852.65,-900.94 2685,-885.5 2005.23,-822.91 1834.31,-811.62 1152,-790.5 964.09,-784.68 902.76,-718.72 729,-790.5 578.83,-852.53 533.89,-899.11 470,-1048.5 457.09,-1078.69 476.43,-2173.33 448.84,-2284.43" />
|
||||
<polygon fill="black" stroke="black" points="3103.68,-1074.6 3112.64,-1080.92 3108.59,-1075.55 3113.5,-1076.5 3113.5,-1076.5 3113.5,-1076.5 3108.59,-1075.55 3114.36,-1072.08 3103.68,-1074.6 3103.68,-1074.6" />
|
||||
<ellipse fill="none" stroke="black" cx="3099.76" cy="-1073.84" rx="4" ry="4" />
|
||||
<path fill="none" stroke="black" d="M3095.37,-1131.04C3037.15,-1112.09 3125,-988.61 3061,-932.5 2963.4,-846.93 848.96,-740.95 729,-790.5 578.83,-852.53 533.89,-899.11 470,-1048.5 457.09,-1078.69 476.43,-2173.33 448.84,-2284.43" />
|
||||
<polygon fill="black" stroke="black" points="3103.59,-1132.15 3112.89,-1137.96 3108.55,-1132.83 3113.5,-1133.5 3113.5,-1133.5 3113.5,-1133.5 3108.55,-1132.83 3114.11,-1129.04 3103.59,-1132.15 3103.59,-1132.15" />
|
||||
<ellipse fill="none" stroke="black" cx="3099.63" cy="-1131.62" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="448.88,-2294.97 440.06,-2290.26 441,-2288.5 449.82,-2293.21 448.88,-2294.97" />
|
||||
<polyline fill="none" stroke="black" points="444,-2293.5 446.35,-2289.09 " />
|
||||
<polygon fill="black" stroke="black" points="451.24,-2290.56 442.41,-2285.85 443.36,-2284.09 452.18,-2288.8 451.24,-2290.56" />
|
||||
<polyline fill="none" stroke="black" points="446.35,-2289.09 448.71,-2284.68 " />
|
||||
<text text-anchor="start" x="1559" y="-823.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<text text-anchor="start" x="1559" y="-813.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_billing_recurrence_registrar_id
|
||||
</text>
|
||||
</g> <!-- claimsentry_105da9f1 -->
|
||||
@@ -4496,13 +4520,13 @@ td.section {
|
||||
</g> <!-- pollmessage_614a523e->contact_8de8cb16 -->
|
||||
<g id="edge17" class="edge">
|
||||
<title>pollmessage_614a523e:w->contact_8de8cb16:e</title>
|
||||
<path fill="none" stroke="black" d="M3845.83,-2876.83C3828.57,-2857.9 3869.78,-2796.41 3837,-2766.5 3709.55,-2650.19 3623.75,-2733.08 3452,-2716.5 2760.49,-2649.73 2576.61,-2735.61 1892,-2617.5 1687.94,-2582.29 1586,-2640.23 1444,-2489.5 1418.71,-2462.66 1452.46,-2434.19 1426,-2408.5 1342.89,-2327.81 1280.04,-2395.69 1170,-2359.5 1146.25,-2351.69 1141.38,-2336.37 1121.7,-2332.44" />
|
||||
<path fill="none" stroke="black" d="M3845.83,-2876.83C3828.57,-2857.9 3869.78,-2796.41 3837,-2766.5 3709.55,-2650.19 3623.75,-2733.08 3452,-2716.5 2760.49,-2649.73 2576.61,-2735.61 1892,-2617.5 1687.94,-2582.29 1586,-2640.23 1444,-2489.5 1418.71,-2462.66 1452.12,-2434.53 1426,-2408.5 1343.51,-2326.28 1281.93,-2384.69 1170,-2352.5 1147.08,-2345.91 1140.72,-2335.05 1121.64,-2332.2" />
|
||||
<polygon fill="black" stroke="black" points="3853.5,-2879.36 3861.59,-2886.77 3858.25,-2880.93 3863,-2882.5 3863,-2882.5 3863,-2882.5 3858.25,-2880.93 3864.41,-2878.23 3853.5,-2879.36 3853.5,-2879.36" />
|
||||
<ellipse fill="none" stroke="black" cx="3849.71" cy="-2878.11" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="1112.04,-2336.57 1112.95,-2326.61 1114.94,-2326.8 1114.03,-2336.75 1112.04,-2336.57" />
|
||||
<polyline fill="none" stroke="black" points="1111.5,-2331.5 1116.48,-2331.96 " />
|
||||
<polygon fill="black" stroke="black" points="1117.02,-2337.03 1117.93,-2327.07 1119.92,-2327.25 1119.01,-2337.21 1117.02,-2337.03" />
|
||||
<polyline fill="none" stroke="black" points="1116.48,-2331.96 1121.46,-2332.41 " />
|
||||
<polygon fill="black" stroke="black" points="1112.15,-2336.56 1112.84,-2326.58 1114.84,-2326.72 1114.15,-2336.69 1112.15,-2336.56" />
|
||||
<polyline fill="none" stroke="black" points="1111.5,-2331.5 1116.49,-2331.84 " />
|
||||
<polygon fill="black" stroke="black" points="1117.14,-2336.9 1117.83,-2326.93 1119.83,-2327.06 1119.14,-2337.04 1117.14,-2336.9" />
|
||||
<polyline fill="none" stroke="black" points="1116.49,-2331.84 1121.48,-2332.19 " />
|
||||
<text text-anchor="start" x="2367" y="-2690.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_poll_message_contact_repo_id
|
||||
</text>
|
||||
@@ -4535,7 +4559,7 @@ td.section {
|
||||
</g> <!-- pollmessage_614a523e->domainhistory_a54cc226 -->
|
||||
<g id="edge42" class="edge">
|
||||
<title>pollmessage_614a523e:w->domainhistory_a54cc226:e</title>
|
||||
<path fill="none" stroke="black" d="M3851.94,-2830.23C3834.77,-2776.71 3842.85,-2589.68 3837,-2563.5 3732.52,-2095.75 3567.96,-2018.66 3470,-1549.5 3463.65,-1519.09 3474.66,-1291.75 3452,-1270.5 3331.06,-1157.11 3244.61,-1263.03 3079,-1270.5 2911.38,-1278.06 2869.56,-1284.18 2703,-1304.5 2695.75,-1305.39 2691.97,-1306.73 2687.3,-1307.61" />
|
||||
<path fill="none" stroke="black" d="M3851.94,-2830.23C3834.76,-2776.71 3842.81,-2589.69 3837,-2563.5 3732.38,-2092.36 3568.5,-2013.96 3470,-1541.5 3463.84,-1511.96 3474.03,-1291.12 3452,-1270.5 3330.96,-1157.23 3244.61,-1263.03 3079,-1270.5 2911.38,-1278.06 2869.56,-1284.18 2703,-1304.5 2695.75,-1305.39 2691.97,-1306.73 2687.3,-1307.61" />
|
||||
<polygon fill="black" stroke="black" points="3856.88,-2836.59 3859.44,-2847.26 3859.94,-2840.55 3863,-2844.5 3863,-2844.5 3863,-2844.5 3859.94,-2840.55 3866.56,-2841.74 3856.88,-2836.59 3856.88,-2836.59" />
|
||||
<ellipse fill="none" stroke="black" cx="3854.43" cy="-2833.43" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="2678.43,-1313.4 2677.56,-1303.43 2679.56,-1303.26 2680.42,-1313.22 2678.43,-1313.4" />
|
||||
@@ -4970,14 +4994,14 @@ td.section {
|
||||
</g> <!-- pollmessage_614a523e->registrar_6e1503e3 -->
|
||||
<g id="edge72" class="edge">
|
||||
<title>pollmessage_614a523e:w->registrar_6e1503e3:e</title>
|
||||
<path fill="none" stroke="black" d="M3857.11,-2541.44C3836.5,-2410.85 3844.9,-1574.11 3837,-1549.5 3748.12,-1272.49 3578.16,-1277.56 3470,-1007.5 3452.07,-962.73 3487.54,-933.1 3452,-900.5 3445.76,-894.78 2245.45,-803.03 2237,-802.5 1755.24,-772.2 1634.59,-764.39 1152,-753.5 964.05,-749.26 916.42,-738.76 729,-753.5 612.81,-762.64 546.46,-706.54 470,-794.5 443.91,-824.52 481.93,-2164.55 449.11,-2284.88" />
|
||||
<path fill="none" stroke="black" d="M3857.07,-2541.3C3836.34,-2409.67 3845.04,-1566.28 3837,-1541.5 3748.1,-1267.59 3577.99,-1274.46 3470,-1007.5 3451.92,-962.8 3487.41,-933.24 3452,-900.5 3421.45,-872.26 3120.43,-871.28 3079,-867.5 2903.85,-851.53 2860.18,-846.15 2685,-830.5 2485.97,-812.71 2436.56,-802.8 2237,-792.5 1876.97,-773.92 1786.47,-782.81 1426,-777.5 1304.23,-775.71 1273.78,-775.17 1152,-774.5 1000.38,-773.67 573.96,-684.13 470,-794.5 442.73,-823.45 481.82,-2164.45 449.11,-2284.87" />
|
||||
<polygon fill="black" stroke="black" points="3859.74,-2549.05 3858.75,-2559.97 3861.37,-2553.77 3863,-2558.5 3863,-2558.5 3863,-2558.5 3861.37,-2553.77 3867.25,-2557.03 3859.74,-2549.05 3859.74,-2549.05" />
|
||||
<ellipse fill="none" stroke="black" cx="3858.43" cy="-2545.27" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="448.81,-2295.19 440.21,-2290.09 441.23,-2288.37 449.83,-2293.47 448.81,-2295.19" />
|
||||
<polyline fill="none" stroke="black" points="444,-2293.5 446.55,-2289.2 " />
|
||||
<polygon fill="black" stroke="black" points="451.36,-2290.89 442.76,-2285.79 443.78,-2284.07 452.38,-2289.17 451.36,-2290.89" />
|
||||
<polyline fill="none" stroke="black" points="446.55,-2289.2 449.1,-2284.9 " />
|
||||
<text text-anchor="start" x="1896" y="-806.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
<polygon fill="black" stroke="black" points="451.36,-2290.88 442.75,-2285.79 443.77,-2284.07 452.38,-2289.16 451.36,-2290.88" />
|
||||
<polyline fill="none" stroke="black" points="446.55,-2289.2 449.09,-2284.89 " />
|
||||
<text text-anchor="start" x="1896" y="-796.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
fk_poll_message_transfer_response_losing_registrar_id
|
||||
</text>
|
||||
</g> <!-- cursor_6af40e8c -->
|
||||
@@ -7368,6 +7392,21 @@ td.section {
|
||||
<td class="minwidth">recurrence_time_of_year</td>
|
||||
<td class="minwidth">text</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"></td>
|
||||
<td class="minwidth">renewal_price_behavior</td>
|
||||
<td class="minwidth">text not null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"></td>
|
||||
<td class="minwidth">renewal_price_currency</td>
|
||||
<td class="minwidth">text</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"></td>
|
||||
<td class="minwidth">renewal_price_amount</td>
|
||||
<td class="minwidth">numeric(19, 2)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3"></td>
|
||||
</tr>
|
||||
|
||||
@@ -112,3 +112,4 @@ V111__add_billingcancellation_missing_indexes.sql
|
||||
V112__add_billingrecurrence_missing_indexes.sql
|
||||
V113__add_host_missing_indexes.sql
|
||||
V114__add_allocation_token_indexes.sql
|
||||
V115__add_renewal_columns_to_billing_recurrence.sql
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
-- Copyright 2022 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.
|
||||
|
||||
alter table "BillingRecurrence" add column renewal_price_behavior text default 'DEFAULT' not null;
|
||||
alter table "BillingRecurrence" add column renewal_price_currency text;
|
||||
alter table "BillingRecurrence" add column renewal_price_amount numeric(19, 2);
|
||||
@@ -116,7 +116,10 @@ CREATE TABLE public."BillingRecurrence" (
|
||||
reason text NOT NULL,
|
||||
domain_name text NOT NULL,
|
||||
recurrence_end_time timestamp with time zone,
|
||||
recurrence_time_of_year text
|
||||
recurrence_time_of_year text,
|
||||
renewal_price_behavior text DEFAULT 'DEFAULT'::text NOT NULL,
|
||||
renewal_price_currency text,
|
||||
renewal_price_amount numeric(19,2)
|
||||
);
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user