1
0
mirror of https://github.com/google/nomulus synced 2026-06-09 16:33:02 +00:00

Compare commits

...

22 Commits

Author SHA1 Message Date
gbrodman 4ec7f23e84 Use the parent to store the history repo ID and fill in the base object (#830)
* Use the parent to store the history repo ID and fill in the base object

Storing the repo ID in the parent and in the base object has two primary
benefits.

First, it unifies the parent information in the HistoryEntry's `parent`
object. This simplifies the builders and the data flow.

Second, when possible (which should be always, post-migration) we fill
out the DomainContent's repo ID (similarly for the other EPP resources)
which means that when reconstituting the ofy keys we don't need to pass
the repo ID in from a separate object. This way, all the data are
encapsulated where they should be.

The primary downside here is that it further reduces the "immutability"
of the history objects (since we're using the Hibernate setter for the
parent repo ID) but we weren't immutable anyway.

* Respond to CR

- compare the entire vkeys in tests
- always return the parent for repo ID

* Simplify creation of parent VKeys

* Fix flipped isAssignableFrom check in VKey

* Merge remote-tracking branch 'origin/master' into historyRepoId
2020-10-09 16:01:51 -04:00
Shicong Huang 7a68b1b6f0 Revert package-lock.json to version from #676 (#834)
Co-authored-by: gbrodman <gbrodman@google.com>
2020-10-09 15:57:23 -04:00
Shicong Huang 14e593d9e1 Add SchemaCrawler as dependency (#833) 2020-10-09 15:02:11 -04:00
Weimin Yu 2d5de96fbd Minor python changes (#832)
* Minor python changes

Use dataclasses instead of attrs. The former is part of the standard lib
while the latter may need to be installed separately.

Also added python3 to the list of prerequisites.
2020-10-09 14:50:21 -04:00
Weimin Yu 13d30b0bfb Maintain a release-to-Version map in deployment (#831)
* Maintain a release-to-Version map in deployment

Keep track of the mapping between Nomulus release tags and AppEngine
version ids with a mapping file. This is necessary because AppEngine
does not support custom versioning. With this mapping, rollbacks could
be automated. Automation of rollbacks is important since there are
test-supporting metadata to be updated, but are easily forgotten.

During the last stage of deployment, current per-service version ids
are fetched using gcloud and are appended to a file on GCS. Each line
is of the format "{RELEASE_TAG},{APPENGINE_SERVICE},{APPENGINE_VERSION}.

This change has been tested in crash. The rollback script is still a
work in progress.
2020-10-09 13:32:52 -04:00
Shicong Huang b05f6b4ba3 Add SQL schema for DelegationSignerData (#713)
* Add SQL schema for DelegationSignerData

* Remove join table

* Rebased on HEAD

* Rebase on head
2020-10-09 10:22:31 -04:00
Shicong Huang 17a1387184 Disable auto-generation on id for HostHistory and ContactHistory (#827) 2020-10-08 12:30:54 -04:00
Ben McIlwain 2e230664fd Convert CertificateViolation into an enum (#829)
* Convert CertificateViolation into an enum

This ends up being nicer to deal with from callsites than class instances, while
still permitting full configurability of all parameters. There are various other
changes/fixes as well.
2020-10-07 22:19:36 -04:00
Michael Muller 299b093f78 Correctly restore composite VKeys in DomainContent (#825)
* Restore composite vkeys in DomainContent

PollMessage/BillingEvent vkeys in DomainContent must have their ofy keys
restored from other fields in DomainContent (namely the repo id and their
specific history event ids).

Add PostLoad methods to DomainContent and DomainHistory to do the restoration.

* Fixes for review.

* Deal with foreign-key cycles
2020-10-07 12:42:01 -04:00
Ben McIlwain 61e7fa89f7 Fix incorrect repackaged App Engine import (#828)
* Fix incorrect repackaged App Engine import
2020-10-07 11:52:17 -04:00
sarahcaseybot 6ab69d4226 Add a CertificateChecker class (#793)
* CertificateChecker with checks for expiration and key length

* Add validity length check

* Get rid of hard-coded constants and DSA checks

* add files that for some reason weren't included in last commit

* Rename violations and other fixes

* Add displayMessage to CertificateViolation enum

* Switch violations from an enum to a class

* small changes

* Get rid of ECDSA checks

* add checks for old validity length

* Change error message for validity length
2020-10-06 15:47:42 -04:00
gbrodman 0f09a4a0ab Add more Datastore/Sql Entity annotations (#826)
* Add more Datastore/Sql Entity annotations

* Move comments up a line
2020-10-05 13:07:53 -04:00
Shicong Huang 95f6ccc657 Fix vkey reconstruction for PollMessage (#823)
* Fix vkey reconstruction for PollMessage

* Add foreign key

* Rebase on HEAD
2020-10-05 10:35:40 -04:00
Michael Muller 77fabe4dc4 Move "WithLongVKey" to BillingEvent subclasses (#821)
When loading the VKeys for the BillingEvents hierarchy, it is necessary to
restore the original concrete class for the type, otherwise we end up with a
different (and incompatible) VKey.

As part of this, convert the cancellation matching billing event to
VKey<Recurring>, which seems like the only thing it actually can be.
2020-10-02 15:20:23 -04:00
Lai Jiang 71fa12f773 Fix invoicing SQL (#824) 2020-10-01 14:29:49 -04:00
Shicong Huang fd40a6a2b9 Use composite primary key for HostHistory and ContactHistory (#809)
* Use composite primary key for HostHistory and ContactHistory

* Update flyway file version

* Make getters private

* Add javadoc

* Rebase on HEAD
2020-10-01 11:01:57 -04:00
Michael Muller 71f86c9970 Add VKey.restoreOfy() method for fixing ofy keys (#820)
Add a restoreOfy() instance method and a restoreOfyFrom() static method to
assist in restoring the objectify key for classes that have composite keys
that do not restore automatically.
2020-09-30 11:15:58 -04:00
Michael Muller 6f75dfd116 Create a flyway index file and verify correctness (#819)
* Create a flyway index file and verify correctness

Create an index file (flyway.txt) containing the names of all of the flyway
files and verify that it is ordered and in sync with the actual contents of
the flyway directory.  Also provide a target (generateFlywayIndex) to
automatically generate it.

The purpose of flyway.txt is to cause a merge conflict in the event that two
different developers add a flyway file with the same sequence number, an event
which has occurred multiple times.
2020-09-29 11:26:05 -04:00
Lai Jiang ad5a74fee9 Revert "Request 101m CPU in sandbox proxy (#813)" (#818)
This reverts commit e30c0f9a11.

The proposed solution didn't work.
2020-09-25 11:55:46 -04:00
Lai Jiang 29b1ec4211 Add log4j-core as a runtime dependency (#817)
Without it we kept getting the following warning:

ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...
2020-09-24 19:59:39 -04:00
Weimin Yu 553d5717cb Enhance the test for forbidden Schema changes (#815)
* Enhance the test for forbidden Schema changes

Current test is git-based. It is difficult to maintain and does not
catch out-of-order version numbers. It is also more aggressive than
necessary, failing on changes to submitted scripts that have not been
deployed yet.

The new test starts a database, deploys the current schema to it,
then deploys the set of Flyway scripts in this repository to the database.
2020-09-24 12:31:08 -04:00
Shicong Huang 1056fdbb64 Fix VKey reconstruction issue in BillingEvent (#805)
* Fix VKey reconstruction issue in BillingEvent

* Rebase on head
2020-09-23 19:04:58 -04:00
141 changed files with 2521 additions and 572 deletions
+2 -2
View File
@@ -191,7 +191,7 @@ allprojects {
}
task runPresubmits(type: Exec) {
executable '/usr/bin/python'
executable '/usr/bin/python3'
args('config/presubmits.py')
}
@@ -260,7 +260,7 @@ subprojects {
// in the 'configurations' block, the following code must run after
// project evaluation, when all configurations have been created.
configurations.each {
if (it.name != 'dependencyLicenseReport') {
if (it.name != 'dependencyLicenseReport' && it.name != 'integration') {
it.resolutionStrategy.activateDependencyLocking()
}
}
+7 -3
View File
@@ -16,7 +16,7 @@
"""
import argparse
import attr
import dataclasses
import io
import os
import shutil
@@ -25,7 +25,7 @@ import sys
from typing import List, Union
@attr.s(auto_attribs=True)
@dataclasses.dataclass
class Property:
name : str = ''
desc : str = ''
@@ -39,7 +39,7 @@ class Property:
raise ValidationError('value of {self.name} must be "true" or '
'"false".')
@attr.s(auto_attribs=True)
@dataclasses.dataclass
class GradleFlag:
flags : Union[str, List[str]]
desc : str
@@ -75,6 +75,7 @@ Pseudo-commands:
"""
# Define all of our special gradle properties here.
# TODO(b/169318491): use consistent naming style for properties and variables.
PROPERTIES = [
Property('mavenUrl',
'URL to use for the main maven repository (defaults to maven '
@@ -124,6 +125,9 @@ PROPERTIES = [
'server/schema integration tests. Please refer to <a '
'href="./integration/README.md">integration project</a> for more '
'information.'),
Property('baseSchemaTag',
'The nomulus version tag of the schema for use in the schema'
'deployment integration test (:db:schemaIncrementalDeployTest)'),
Property('schema_version',
'The nomulus version tag of the schema for use in a database'
'integration test.'),
+90
View File
@@ -18,6 +18,7 @@ Error Prone) so we must write them manually.
"""
import os
from typing import List, Tuple
import sys
import re
@@ -178,6 +179,90 @@ PRESUBMITS = {
"JavaScript files should not include console logging."
}
# Note that this regex only works for one kind of Flyway file. If we want to
# start using "R" and "U" files we'll need to update this script.
FLYWAY_FILE_RX = re.compile(r'V(\d+)__.*')
def get_seqnum(filename: str, location: str) -> int:
"""Extracts the sequence number from a filename."""
m = FLYWAY_FILE_RX.match(filename)
if m is None:
raise ValueError('Illegal Flyway filename: %s in %s' % (filename, location))
return int(m.group(1))
def files_by_seqnum(files: List[str], location: str) -> List[Tuple[int, str]]:
"""Returns the list of seqnum, filename sorted by sequence number."""
return [(get_seqnum(filename, location), filename) for filename in files]
def has_valid_order(indexed_files: List[Tuple[int, str]], location: str) -> bool:
"""Verify that sequence numbers are in order without gaps or duplicates.
Args:
files: List of seqnum, filename for a list of Flyway files.
location: Where the list of files came from (for error reporting).
Returns:
True if the file list is valid.
"""
last_index = 0
valid = True
for seqnum, filename in indexed_files:
if seqnum == last_index:
print('duplicate Flyway file sequence number found in %s: %s' %
(location, filename))
valid = False
elif seqnum < last_index:
print('File %s in %s is out of order.' % (filename, location))
valid = False
elif seqnum != last_index + 1:
print('Missing Flyway sequence number %d in %s. Next file is %s' %
(last_index + 1, location, filename))
valid = False
last_index = seqnum
return valid
def verify_flyway_index():
"""Verifies that the Flyway index file is in sync with the directory."""
success = True
# Sort the files in the Flyway directory by their sequence number.
files = sorted(
files_by_seqnum(os.listdir('db/src/main/resources/sql/flyway'),
'Flyway directory'))
# Make sure that there are no gaps and no duplicate sequence numbers in the
# files themselves.
if not has_valid_order(files, 'Flyway directory'):
success = False
# Remove the sequence numbers and compare against the index file contents.
files = [filename[1] for filename in sorted(files)]
with open('db/src/main/resources/sql/flyway.txt') as index:
indexed_files = index.read().splitlines()
if files != indexed_files:
unindexed = set(files) - set(indexed_files)
if unindexed:
print('The following Flyway files are not in flyway.txt: %s' % unindexed)
nonexistent = set(indexed_files) - set(files)
if nonexistent:
print('The following files are in flyway.txt but not in the Flyway '
'directory: %s' % nonexistent)
# Do an ordering check on the index file (ignore the result, we're failing
# anyway).
has_valid_order(files_by_seqnum(indexed_files, 'flyway.txt'), 'flyway.txt')
success = False
if not success:
print('Please fix any conflicts and run "./nom_build :db:generateFlywayIndex"')
return not success
def get_files():
for root, dirnames, filenames in os.walk("."):
@@ -197,5 +282,10 @@ if __name__ == "__main__":
failed = True
print("%s had errors: \n %s" % (file, "\n ".join(error_messages)))
# And now for something completely different: check to see if the Flyway
# index is up-to-date. It's quicker to do it here than in the unit tests:
# when we put it here it fails fast before all of the tests are run.
failed |= verify_flyway_index()
if failed:
sys.exit(1)
+3
View File
@@ -245,6 +245,7 @@ dependencies {
testCompile deps['org.apache.ftpserver:ftpserver-core']
compile deps['org.apache.httpcomponents:httpclient']
compile deps['org.apache.httpcomponents:httpcore']
runtime deps['org.apache.logging.log4j:log4j-core']
testCompile deps['org.apache.sshd:sshd-core']
testCompile deps['org.apache.sshd:sshd-scp']
testCompile deps['org.apache.sshd:sshd-sftp']
@@ -269,6 +270,8 @@ dependencies {
compile deps['org.testcontainers:postgresql']
testCompile deps['org.testcontainers:selenium']
testCompile deps['org.testcontainers:testcontainers']
compile deps['us.fatehi:schemacrawler']
compile deps['us.fatehi:schemacrawler-diagram']
compile deps['xerces:xmlParserAPIs']
compile deps['xpp3:xpp3']
// This dependency must come after javax.mail:mail as it would otherwise
@@ -202,6 +202,7 @@ org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +250,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -200,6 +200,7 @@ org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -246,5 +247,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -201,8 +201,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -251,5 +253,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -200,8 +200,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +251,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -202,6 +202,7 @@ org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +250,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -201,6 +201,7 @@ org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -248,5 +249,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -200,8 +200,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +251,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -200,8 +200,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +251,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -200,8 +200,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +251,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -200,8 +200,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +251,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -298,5 +298,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -297,5 +297,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -216,7 +216,8 @@ org.apache.ftpserver:ftplet-api:1.0.6
org.apache.ftpserver:ftpserver-core:1.0.6
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.apache.mina:mina-core:2.0.4
org.apache.sshd:sshd-core:2.0.0
org.apache.sshd:sshd-scp:2.0.0
@@ -301,5 +302,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -216,7 +216,8 @@ org.apache.ftpserver:ftplet-api:1.0.6
org.apache.ftpserver:ftpserver-core:1.0.6
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.apache.mina:mina-core:2.0.4
org.apache.sshd:sshd-core:2.0.0
org.apache.sshd:sshd-scp:2.0.0
@@ -302,5 +303,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -55,7 +55,7 @@ FROM (
FROM
`%PROJECT_ID%.%DATASTORE_EXPORT_DATA_SET%.%REGISTRY_TABLE%`
WHERE
enableInvoicing IS TRUE) ) AS BillingEvent
invoicingEnabled IS TRUE) ) AS BillingEvent
-- Gather billing ID from registrar table
-- This is a 'JOIN' as opposed to 'LEFT JOIN' to filter out
-- non-billable registrars
@@ -67,6 +67,7 @@ import google.registry.model.domain.DomainCommand.Update.AddRemove;
import google.registry.model.domain.DomainCommand.Update.Change;
import google.registry.model.domain.fee.FeeUpdateCommandExtension;
import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.domain.secdns.SecDnsUpdateExtension;
import google.registry.model.domain.superuser.DomainUpdateSuperuserExtension;
import google.registry.model.eppcommon.AuthInfo;
@@ -238,10 +239,16 @@ public final class DomainUpdateFlow implements TransactionalFlow {
DomainBase.Builder domainBuilder =
domain
.asBuilder()
// Handle the secDNS extension.
// Handle the secDNS extension. As dsData in secDnsUpdate is read from EPP input and
// does not have domainRepoId set, we create a copy of the existing dsData without
// domainRepoId for comparison.
.setDsData(
secDnsUpdate.isPresent()
? updateDsData(domain.getDsData(), secDnsUpdate.get())
? updateDsData(
domain.getDsData().stream()
.map(DelegationSignerData::cloneWithoutDomainRepoId)
.collect(toImmutableSet()),
secDnsUpdate.get())
: domain.getDsData())
.setLastEppUpdateTime(now)
.setLastEppUpdateClientId(clientId)
@@ -252,7 +252,8 @@ public final class OteAccountBuilder {
private void saveAllEntities() {
tm().assertInTransaction();
ImmutableList<Registry> registries = ImmutableList.of(sunriseTld, gaTld, eapTld);
// use ImmutableObject instead of Registry so that the Key generation doesn't break
ImmutableList<ImmutableObject> registries = ImmutableList.of(sunriseTld, gaTld, eapTld);
ImmutableList<RegistrarContact> contacts = contactsBuilder.build();
if (!replaceExisting) {
@@ -33,12 +33,14 @@ import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.IgnoreSave;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.OnLoad;
import com.googlecode.objectify.annotation.Parent;
import com.googlecode.objectify.condition.IfNull;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.common.TimeOfYear;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.domain.token.AllocationToken;
@@ -57,16 +59,14 @@ import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.MappedSuperclass;
import javax.persistence.PostLoad;
import javax.persistence.Transient;
import org.joda.money.Money;
import org.joda.time.DateTime;
/** A billable event in a domain's lifecycle. */
@MappedSuperclass
@WithLongVKey
public abstract class BillingEvent extends ImmutableObject
implements Buildable, TransferServerApproveEntity {
@@ -108,10 +108,7 @@ public abstract class BillingEvent extends ImmutableObject
}
/** Entity id. */
@Id
@javax.persistence.Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
@Id @javax.persistence.Id Long id;
@Parent @DoNotHydrate @Transient Key<HistoryEntry> parent;
@@ -149,6 +146,21 @@ public abstract class BillingEvent extends ImmutableObject
@Nullable
Set<Flag> flags;
@PostLoad
void postLoad() {
parent =
Key.create(
Key.create(DomainBase.class, domainRepoId),
HistoryEntry.class,
domainHistoryRevisionId);
}
@OnLoad
void onLoad() {
domainHistoryRevisionId = parent.getId();
domainRepoId = parent.getParent().getName();
}
public String getClientId() {
return clientId;
}
@@ -245,7 +257,6 @@ public abstract class BillingEvent extends ImmutableObject
}
public B setParent(Key<HistoryEntry> parentKey) {
// TODO(shicong): Figure out how to set domainHistoryRevisionId and domainRepoId
getInstance().parent = parentKey;
return thisCastToDerived();
}
@@ -258,6 +269,11 @@ public abstract class BillingEvent extends ImmutableObject
checkNotNull(instance.eventTime, "Event time must be set");
checkNotNull(instance.targetId, "Target ID must be set");
checkNotNull(instance.parent, "Parent must be set");
checkNotNull(instance.parent.getParent(), "parent.getParent() must be set");
checkNotNull(
instance.parent.getParent().getName(), "parent.getParent().getName() must be set");
instance.domainHistoryRevisionId = instance.parent.getId();
instance.domainRepoId = instance.parent.getParent().getName();
return super.build();
}
}
@@ -275,6 +291,7 @@ public abstract class BillingEvent extends ImmutableObject
@javax.persistence.Index(columnList = "allocation_token_id")
})
@AttributeOverride(name = "id", column = @Column(name = "billing_event_id"))
@WithLongVKey
public static class OneTime extends BillingEvent implements DatastoreAndSqlEntity {
/** The billable value. */
@@ -310,7 +327,7 @@ public abstract class BillingEvent extends ImmutableObject
* Cancellation}s.
*/
@Column(name = "cancellation_matching_billing_recurrence_id")
VKey<? extends BillingEvent> cancellationMatchingBillingEvent;
VKey<Recurring> cancellationMatchingBillingEvent;
/**
* The {@link AllocationToken} used in the creation of this event, or null if one was not used.
@@ -392,7 +409,7 @@ public abstract class BillingEvent extends ImmutableObject
}
public Builder setCancellationMatchingBillingEvent(
VKey<? extends BillingEvent> cancellationMatchingBillingEvent) {
VKey<Recurring> cancellationMatchingBillingEvent) {
getInstance().cancellationMatchingBillingEvent = cancellationMatchingBillingEvent;
return this;
}
@@ -451,6 +468,7 @@ public abstract class BillingEvent extends ImmutableObject
@javax.persistence.Index(columnList = "recurrence_time_of_year")
})
@AttributeOverride(name = "id", column = @Column(name = "billing_recurrence_id"))
@WithLongVKey
public static class Recurring extends BillingEvent implements DatastoreAndSqlEntity {
/**
@@ -545,6 +563,7 @@ public abstract class BillingEvent extends ImmutableObject
@javax.persistence.Index(columnList = "billingTime")
})
@AttributeOverride(name = "id", column = @Column(name = "billing_cancellation_id"))
@WithLongVKey
public static class Cancellation extends BillingEvent implements DatastoreAndSqlEntity {
/** The billing time of the charge that is being cancelled. */
@@ -665,6 +684,7 @@ public abstract class BillingEvent extends ImmutableObject
/** An event representing a modification of an existing one-time billing event. */
@ReportedOn
@Entity
@WithLongVKey
public static class Modification extends BillingEvent implements DatastoreAndSqlEntity {
/** The change in cost that should be applied to the original billing event. */
@@ -14,20 +14,24 @@
package google.registry.model.contact;
import com.google.common.collect.ImmutableList;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.EntitySubclass;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.contact.ContactHistory.ContactHistoryId;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
import google.registry.schema.replay.DatastoreEntity;
import google.registry.schema.replay.SqlEntity;
import java.io.Serializable;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.PostLoad;
/**
@@ -47,16 +51,25 @@ import javax.persistence.PostLoad;
})
@EntitySubclass
@Access(AccessType.FIELD)
public class ContactHistory extends HistoryEntry {
@IdClass(ContactHistoryId.class)
public class ContactHistory extends HistoryEntry implements SqlEntity {
// Store ContactBase instead of ContactResource so we don't pick up its @Id
@Nullable ContactBase contactBase;
@Column(nullable = false)
VKey<ContactResource> contactRepoId;
@Id
@Access(AccessType.PROPERTY)
public String getContactRepoId() {
return parent.getName();
}
/** This method is private because it is only used by Hibernate. */
@SuppressWarnings("unused")
private void setContactRepoId(String contactRepoId) {
parent = Key.create(ContactResource.class, contactRepoId);
}
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TempHistorySequenceGenerator")
@Column(name = "historyRevisionId")
@Access(AccessType.PROPERTY)
@Override
@@ -75,8 +88,14 @@ public class ContactHistory extends HistoryEntry {
}
/** The key to the {@link ContactResource} this is based off of. */
public VKey<ContactResource> getContactRepoId() {
return contactRepoId;
public VKey<ContactResource> getParentVKey() {
return VKey.create(ContactResource.class, getContactRepoId());
}
/** Creates a {@link VKey} instance for this entity. */
public VKey<ContactHistory> createVKey() {
return VKey.create(
ContactHistory.class, new ContactHistoryId(getContactRepoId(), getId()), Key.create(this));
}
@PostLoad
@@ -86,11 +105,70 @@ public class ContactHistory extends HistoryEntry {
if (contactBase != null && contactBase.getContactId() == null) {
contactBase = null;
}
// Fill in the full, symmetric, parent repo ID key
Key<ContactResource> parentKey =
Key.create(ContactResource.class, (String) contactRepoId.getSqlKey());
parent = parentKey;
contactRepoId = VKey.create(ContactResource.class, contactRepoId.getSqlKey(), parentKey);
}
// In Datastore, save as a HistoryEntry object regardless of this object's type
@Override
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
return ImmutableList.of(asHistoryEntry());
}
/** Class to represent the composite primary key of {@link ContactHistory} entity. */
static class ContactHistoryId extends ImmutableObject implements Serializable {
private String contactRepoId;
private Long id;
/** Hibernate requires this default constructor. */
private ContactHistoryId() {}
ContactHistoryId(String contactRepoId, long id) {
this.contactRepoId = contactRepoId;
this.id = id;
}
/**
* Returns the contact repository id.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private String getContactRepoId() {
return contactRepoId;
}
/**
* Returns the history revision id.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private long getId() {
return id;
}
/**
* Sets the contact repository id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setContactRepoId(String contactRepoId) {
this.contactRepoId = contactRepoId;
}
/**
* Sets the history revision id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setId(long id) {
this.id = id;
}
}
@Override
@@ -111,18 +189,8 @@ public class ContactHistory extends HistoryEntry {
return this;
}
public Builder setContactRepoId(VKey<ContactResource> contactRepoId) {
getInstance().contactRepoId = contactRepoId;
contactRepoId.maybeGetOfyKey().ifPresent(parent -> getInstance().parent = parent);
return this;
}
// We can remove this once all HistoryEntries are converted to History objects
@Override
public Builder setParent(Key<? extends EppResource> parent) {
super.setParent(parent);
getInstance().contactRepoId =
VKey.create(ContactResource.class, parent.getName(), (Key<ContactResource>) parent);
public Builder setContactRepoId(String contactRepoId) {
getInstance().parent = Key.create(ContactResource.class, contactRepoId);
return this;
}
}
@@ -51,7 +51,6 @@ public class ContactResource extends ContactBase
@Override
public VKey<ContactResource> createVKey() {
// TODO(mmuller): create symmetric keys if we can ever reload both sides.
return VKey.create(ContactResource.class, getRepoId(), Key.create(this));
}
@@ -19,6 +19,7 @@ import google.registry.model.EppResource;
import google.registry.model.EppResource.ForeignKeyedEppResource;
import google.registry.model.annotations.ExternalMessagingName;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.host.HostResource;
import google.registry.persistence.VKey;
import google.registry.persistence.WithStringVKey;
@@ -106,6 +107,32 @@ public class DomainBase extends DomainContent
return gracePeriods;
}
/**
* Returns the set of {@link DelegationSignerData} associated with the domain.
*
* <p>This is the getter method specific for Hibernate to access the field so it is set to
* private. The caller can use the public {@link #getDsData()} to get the DS data.
*
* <p>Note that we need to set `insertable = false, updatable = false` for @JoinColumn, otherwise
* Hibernate would try to set the foreign key to null(through an UPDATE TABLE sql) instead of
* deleting the whole entry from the table when the {@link DelegationSignerData} is removed from
* the set.
*/
@Access(AccessType.PROPERTY)
@OneToMany(
cascade = {CascadeType.ALL},
fetch = FetchType.EAGER,
orphanRemoval = true)
@JoinColumn(
name = "domainRepoId",
referencedColumnName = "repoId",
insertable = false,
updatable = false)
@SuppressWarnings("UnusedMethod")
private Set<DelegationSignerData> getInternalDelegationSignerData() {
return dsData;
}
@Override
public VKey<DomainBase> createVKey() {
return VKey.create(DomainBase.class, getRepoId(), Key.create(this));
@@ -35,11 +35,13 @@ import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
import static google.registry.util.DomainNameUtils.getTldFromDomainName;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.IgnoreSave;
import com.googlecode.objectify.annotation.Index;
@@ -49,6 +51,7 @@ import google.registry.flows.ResourceFlowUtils;
import google.registry.model.EppResource;
import google.registry.model.EppResource.ResourceWithTransferData;
import google.registry.model.billing.BillingEvent;
import google.registry.model.common.EntityGroupRoot;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.launch.LaunchNotice;
import google.registry.model.domain.rgp.GracePeriodStatus;
@@ -57,6 +60,7 @@ import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.HostResource;
import google.registry.model.poll.PollMessage;
import google.registry.model.registry.Registry;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.DomainTransferData;
import google.registry.model.transfer.TransferStatus;
import google.registry.persistence.VKey;
@@ -65,7 +69,6 @@ import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import javax.persistence.Access;
import javax.persistence.AccessType;
@@ -209,6 +212,15 @@ public class DomainContent extends EppResource
@Column(name = "deletion_poll_message_id")
VKey<PollMessage.OneTime> deletePollMessage;
/**
* History record for the delete poll message.
*
* <p>Here so we can restore the original ofy key from sql.
*/
@Column(name = "deletion_poll_message_history_id")
@Ignore
Long deletePollMessageHistoryId;
/**
* The recurring billing event associated with this domain's autorenewals.
*
@@ -220,6 +232,15 @@ public class DomainContent extends EppResource
@Column(name = "billing_recurrence_id")
VKey<BillingEvent.Recurring> autorenewBillingEvent;
/**
* History record for the autorenew billing event.
*
* <p>Here so we can restore the original ofy key from sql.
*/
@Column(name = "billing_recurrence_history_id")
@Ignore
Long autorenewBillingEventHistoryId;
/**
* The recurring poll message associated with this domain's autorenewals.
*
@@ -231,6 +252,13 @@ public class DomainContent extends EppResource
@Column(name = "autorenew_poll_message_id")
VKey<PollMessage.Autorenew> autorenewPollMessage;
/**
* History record for the autorenew poll message.
*
* <p>Here so we can restore the original ofy key from sql.
*/
@Ignore Long autorenewPollMessageHistoryId;
/** The unexpired grace periods for this domain (some of which may not be active yet). */
@Transient Set<GracePeriod> gracePeriods;
@@ -284,10 +312,20 @@ public class DomainContent extends EppResource
nullToEmptyImmutableCopy(gracePeriods).stream()
.map(gracePeriod -> gracePeriod.cloneWithDomainRepoId(getRepoId()))
.collect(toImmutableSet());
// Restore history record ids.
autorenewPollMessageHistoryId = getHistoryId(autorenewPollMessage);
autorenewBillingEventHistoryId = getHistoryId(autorenewBillingEvent);
deletePollMessageHistoryId = getHistoryId(deletePollMessage);
dsData =
nullToEmptyImmutableCopy(dsData).stream()
.map(dsData -> dsData.cloneWithDomainRepoId(getRepoId()))
.collect(toImmutableSet());
}
@PostLoad
void postLoad() {
@SuppressWarnings("UnusedMethod")
private final void postLoad() {
// Reconstitute the contact list.
ImmutableSet.Builder<DesignatedContact> contactsBuilder = new ImmutableSet.Builder<>();
@@ -306,6 +344,23 @@ public class DomainContent extends EppResource
}
allContacts = contactsBuilder.build();
// Reconstitute the composite ofy keys from the SQL data.
Key<DomainBase> myKey = Key.create(DomainBase.class, getRepoId());
deletePollMessage = restoreOfyFrom(myKey, deletePollMessage, deletePollMessageHistoryId);
autorenewBillingEvent =
restoreOfyFrom(myKey, autorenewBillingEvent, autorenewBillingEventHistoryId);
autorenewPollMessage =
restoreOfyFrom(myKey, autorenewPollMessage, autorenewPollMessageHistoryId);
}
private <T> VKey<T> restoreOfyFrom(Key<DomainBase> domainKey, VKey<T> key, Long historyId) {
if (historyId == null) {
// This is a legacy key (or a null key, in which case this works too)
return VKey.restoreOfyFrom(key, EntityGroupRoot.class, "per-tld");
} else {
return VKey.restoreOfyFrom(key, domainKey, HistoryEntry.class, historyId);
}
}
public ImmutableSet<String> getSubordinateHosts() {
@@ -387,6 +442,12 @@ public class DomainContent extends EppResource
this.gracePeriods = gracePeriods;
}
// Hibernate needs this in order to populate dsData but no one else should ever use it
@SuppressWarnings("UnusedMethod")
private void setInternalDelegationSignerData(Set<DelegationSignerData> dsData) {
this.dsData = dsData;
}
public final String getCurrentSponsorClientId() {
return getPersistedCurrentSponsorClientId();
}
@@ -655,13 +716,24 @@ public class DomainContent extends EppResource
+ " use DomainBase instead");
}
private static Long getHistoryId(VKey<?> key) {
if (key == null) {
return null;
}
Key<?> parent = key.getOfyKey().getParent();
if (parent == null || parent.getKind().equals("EntityGroupRoot")) {
return null;
}
return parent.getId();
}
/** Predicate to determine if a given {@link DesignatedContact} is the registrant. */
static final Predicate<DesignatedContact> IS_REGISTRANT =
(DesignatedContact contact) -> DesignatedContact.Type.REGISTRANT.equals(contact.type);
/** An override of {@link EppResource#asBuilder} with tighter typing. */
@Override
public Builder asBuilder() {
public Builder<? extends DomainContent, ?> asBuilder() {
return new Builder<>(clone(this));
}
@@ -698,10 +770,16 @@ public class DomainContent extends EppResource
instance.tld = getTldFromDomainName(instance.fullyQualifiedDomainName);
T newDomain = super.build();
// Hibernate throws exception if gracePeriods is null because we enabled all cascadable
// operations and orphan removal.
// Hibernate throws exception if gracePeriods or dsData is null because we enabled all
// cascadable operations and orphan removal.
newDomain.gracePeriods =
newDomain.gracePeriods == null ? ImmutableSet.of() : newDomain.gracePeriods;
newDomain.dsData =
newDomain.dsData == null
? ImmutableSet.of()
: newDomain.dsData.stream()
.map(ds -> ds.cloneWithDomainRepoId(instance.getRepoId()))
.collect(toImmutableSet());
return newDomain;
}
@@ -824,16 +902,19 @@ public class DomainContent extends EppResource
public B setDeletePollMessage(VKey<PollMessage.OneTime> deletePollMessage) {
getInstance().deletePollMessage = deletePollMessage;
getInstance().deletePollMessageHistoryId = getHistoryId(deletePollMessage);
return thisCastToDerived();
}
public B setAutorenewBillingEvent(VKey<BillingEvent.Recurring> autorenewBillingEvent) {
getInstance().autorenewBillingEvent = autorenewBillingEvent;
getInstance().autorenewBillingEventHistoryId = getHistoryId(autorenewBillingEvent);
return thisCastToDerived();
}
public B setAutorenewPollMessage(VKey<PollMessage.Autorenew> autorenewPollMessage) {
getInstance().autorenewPollMessage = autorenewPollMessage;
getInstance().autorenewPollMessageHistoryId = getHistoryId(autorenewPollMessage);
return thisCastToDerived();
}
@@ -16,16 +16,18 @@ package google.registry.model.domain;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import com.google.common.collect.ImmutableList;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.EntitySubclass;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.host.HostResource;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
import google.registry.schema.replay.DatastoreEntity;
import google.registry.schema.replay.SqlEntity;
import java.io.Serializable;
import java.util.Optional;
import java.util.Set;
@@ -38,8 +40,6 @@ import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Index;
@@ -67,12 +67,22 @@ import javax.persistence.Table;
@EntitySubclass
@Access(AccessType.FIELD)
@IdClass(DomainHistoryId.class)
public class DomainHistory extends HistoryEntry {
public class DomainHistory extends HistoryEntry implements SqlEntity {
// Store DomainContent instead of DomainBase so we don't pick up its @Id
@Nullable DomainContent domainContent;
@Id String domainRepoId;
@Id
@Access(AccessType.PROPERTY)
public String getDomainRepoId() {
return parent.getName();
}
/** This method is private because it is only used by Hibernate. */
@SuppressWarnings("unused")
private void setDomainRepoId(String domainRepoId) {
parent = Key.create(DomainBase.class, domainRepoId);
}
// We could have reused domainContent.nsHosts here, but Hibernate throws a weird exception after
// we change to use a composite primary key.
@@ -128,7 +138,6 @@ public class DomainHistory extends HistoryEntry {
}
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TempHistorySequenceGenerator")
@Column(name = "historyRevisionId")
@Access(AccessType.PROPERTY)
@Override
@@ -152,12 +161,14 @@ public class DomainHistory extends HistoryEntry {
}
/** The key to the {@link DomainBase} this is based off of. */
public VKey<DomainBase> getDomainRepoId() {
return VKey.create(DomainBase.class, domainRepoId, Key.create(DomainBase.class, domainRepoId));
public VKey<DomainBase> getParentVKey() {
return VKey.create(DomainBase.class, getDomainRepoId());
}
/** Creates a {@link VKey} instance for this entity. */
public VKey<DomainHistory> createVKey() {
return VKey.createSql(DomainHistory.class, new DomainHistoryId(domainRepoId, getId()));
return VKey.create(
DomainHistory.class, new DomainHistoryId(getDomainRepoId(), getId()), Key.create(this));
}
@PostLoad
@@ -168,9 +179,18 @@ public class DomainHistory extends HistoryEntry {
// domainContent with a null object. Unfortunately, the updateTimestamp is never null in SQL.
if (domainContent.getDomainName() == null) {
domainContent = null;
} else {
if (domainContent.getRepoId() == null) {
domainContent = domainContent.asBuilder().setRepoId(parent.getName()).build();
}
}
}
parent = Key.create(DomainBase.class, domainRepoId);
}
// In Datastore, save as a HistoryEntry object regardless of this object's type
@Override
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
return ImmutableList.of(asHistoryEntry());
}
/** Class to represent the composite primary key of {@link DomainHistory} entity. */
@@ -188,19 +208,45 @@ public class DomainHistory extends HistoryEntry {
this.id = id;
}
String getDomainRepoId() {
/**
* Returns the domain repository id.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private String getDomainRepoId() {
return domainRepoId;
}
void setDomainRepoId(String domainRepoId) {
this.domainRepoId = domainRepoId;
}
long getId() {
/**
* Returns the history revision id.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private long getId() {
return id;
}
void setId(long id) {
/**
* Sets the domain repository id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setDomainRepoId(String domainRepoId) {
this.domainRepoId = domainRepoId;
}
/**
* Sets the history revision id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setId(long id) {
this.id = id;
}
}
@@ -227,17 +273,8 @@ public class DomainHistory extends HistoryEntry {
}
public Builder setDomainRepoId(String domainRepoId) {
getInstance().domainRepoId = domainRepoId;
getInstance().parent = Key.create(DomainBase.class, domainRepoId);
return this;
}
// We can remove this once all HistoryEntries are converted to History objects
@Override
public Builder setParent(Key<? extends EppResource> parent) {
super.setParent(parent);
getInstance().domainRepoId = parent.getName();
return this;
}
}
}
@@ -22,6 +22,7 @@ import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.persistence.VKey;
import google.registry.schema.replay.DatastoreAndSqlEntity;
import javax.annotation.Nullable;
import javax.persistence.Entity;
import javax.persistence.Index;
@@ -37,7 +38,7 @@ import org.joda.time.DateTime;
@Embed
@Entity
@Table(indexes = @Index(columnList = "domainRepoId"))
public class GracePeriod extends GracePeriodBase {
public class GracePeriod extends GracePeriodBase implements DatastoreAndSqlEntity {
private static GracePeriod createInternal(
GracePeriodStatus type,
@@ -14,10 +14,22 @@
package google.registry.model.domain.secdns;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.ImmutableObject;
import google.registry.model.domain.secdns.DelegationSignerData.DelegationSignerDataId;
import google.registry.schema.replay.DatastoreAndSqlEntity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.IdClass;
import javax.persistence.Index;
import javax.persistence.Table;
import javax.xml.bind.DatatypeConverter;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@@ -30,19 +42,26 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
*/
@Embed
@XmlType(name = "dsData")
@javax.persistence.Entity
public class DelegationSignerData extends ImmutableObject {
@Entity
@Table(indexes = @Index(columnList = "domainRepoId"))
@IdClass(DelegationSignerDataId.class)
public class DelegationSignerData extends ImmutableObject implements DatastoreAndSqlEntity {
private DelegationSignerData() {}
@Ignore @XmlTransient @javax.persistence.Id String domainRepoId;
/** The identifier for this particular key in the domain. */
@javax.persistence.Id int keyTag;
@javax.persistence.Id
@Column(nullable = false)
int keyTag;
/**
* The algorithm used by this key.
*
* @see <a href="http://tools.ietf.org/html/rfc4034#appendix-A.1">RFC 4034 Appendix A.1</a>
*/
@Column(nullable = false)
@XmlElement(name = "alg")
int algorithm;
@@ -51,6 +70,7 @@ public class DelegationSignerData extends ImmutableObject {
*
* @see <a href="http://tools.ietf.org/html/rfc4034#appendix-A.2">RFC 4034 Appendix A.2</a>
*/
@Column(nullable = false)
int digestType;
/**
@@ -58,6 +78,7 @@ public class DelegationSignerData extends ImmutableObject {
*
* @see <a href="http://tools.ietf.org/html/rfc4034#section-5.1.4">RFC 4034 Section 5.1.4</a>
*/
@Column(nullable = false)
@XmlJavaTypeAdapter(HexBinaryAdapter.class)
byte[] digest;
@@ -81,16 +102,34 @@ public class DelegationSignerData extends ImmutableObject {
return digest == null ? "" : DatatypeConverter.printHexBinary(digest);
}
public DelegationSignerData cloneWithDomainRepoId(String domainRepoId) {
DelegationSignerData clone = clone(this);
clone.domainRepoId = checkArgumentNotNull(domainRepoId);
return clone;
}
public DelegationSignerData cloneWithoutDomainRepoId() {
DelegationSignerData clone = clone(this);
clone.domainRepoId = null;
return clone;
}
public static DelegationSignerData create(
int keyTag, int algorithm, int digestType, byte[] digest) {
int keyTag, int algorithm, int digestType, byte[] digest, String domainRepoId) {
DelegationSignerData instance = new DelegationSignerData();
instance.keyTag = keyTag;
instance.algorithm = algorithm;
instance.digestType = digestType;
instance.digest = digest;
instance.domainRepoId = domainRepoId;
return instance;
}
public static DelegationSignerData create(
int keyTag, int algorithm, int digestType, byte[] digest) {
return create(keyTag, algorithm, digestType, digest, null);
}
public static DelegationSignerData create(
int keyTag, int algorithm, int digestType, String digestAsHex) {
return create(keyTag, algorithm, digestType, DatatypeConverter.parseHexBinary(digestAsHex));
@@ -106,4 +145,20 @@ public class DelegationSignerData extends ImmutableObject {
"%d %d %d %s",
this.keyTag, this.algorithm, this.digestType, DatatypeConverter.printHexBinary(digest));
}
static class DelegationSignerDataId extends ImmutableObject implements Serializable {
String domainRepoId;
int keyTag;
private DelegationSignerDataId() {}
private DelegationSignerDataId(String domainRepoId, int keyTag) {
this.domainRepoId = domainRepoId;
this.keyTag = keyTag;
}
public static DelegationSignerDataId create(String domainRepoId, int keyTag) {
return new DelegationSignerDataId(checkArgumentNotNull(domainRepoId), keyTag);
}
}
}
@@ -20,8 +20,10 @@ import static com.google.common.base.Strings.nullToEmpty;
import com.google.common.collect.ImmutableSet;
import google.registry.model.EppResource;
import google.registry.model.contact.ContactBase;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainContent;
import google.registry.model.host.HostBase;
import google.registry.model.host.HostResource;
import google.registry.model.translators.EnumToAttributeAdapter.EppEnum;
@@ -128,9 +130,15 @@ public enum StatusValue implements EppEnum {
/** Enum to help clearly list which resource types a status value is allowed to be present on. */
private enum AllowedOn {
ALL(ContactResource.class, DomainBase.class, HostBase.class, HostResource.class),
ALL(
ContactBase.class,
ContactResource.class,
DomainContent.class,
DomainBase.class,
HostBase.class,
HostResource.class),
NONE,
DOMAINS(DomainBase.class);
DOMAINS(DomainContent.class, DomainBase.class);
private final ImmutableSet<Class<? extends EppResource>> classes;
@@ -14,20 +14,24 @@
package google.registry.model.host;
import com.google.common.collect.ImmutableList;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.EntitySubclass;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.host.HostHistory.HostHistoryId;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
import google.registry.schema.replay.DatastoreEntity;
import google.registry.schema.replay.SqlEntity;
import java.io.Serializable;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.PostLoad;
/**
@@ -48,16 +52,25 @@ import javax.persistence.PostLoad;
})
@EntitySubclass
@Access(AccessType.FIELD)
public class HostHistory extends HistoryEntry {
@IdClass(HostHistoryId.class)
public class HostHistory extends HistoryEntry implements SqlEntity {
// Store HostBase instead of HostResource so we don't pick up its @Id
@Nullable HostBase hostBase;
@Column(nullable = false)
VKey<HostResource> hostRepoId;
@Id
@Access(AccessType.PROPERTY)
public String getHostRepoId() {
return parent.getName();
}
/** This method is private because it is only used by Hibernate. */
@SuppressWarnings("unused")
private void setHostRepoId(String hostRepoId) {
parent = Key.create(HostResource.class, hostRepoId);
}
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TempHistorySequenceGenerator")
@Column(name = "historyRevisionId")
@Access(AccessType.PROPERTY)
@Override
@@ -76,8 +89,14 @@ public class HostHistory extends HistoryEntry {
}
/** The key to the {@link google.registry.model.host.HostResource} this is based off of. */
public VKey<HostResource> getHostRepoId() {
return hostRepoId;
public VKey<HostResource> getParentVKey() {
return VKey.create(HostResource.class, getHostRepoId());
}
/** Creates a {@link VKey} instance for this entity. */
public VKey<HostHistory> createVKey() {
return VKey.create(
HostHistory.class, new HostHistoryId(getHostRepoId(), getId()), Key.create(this));
}
@PostLoad
@@ -87,10 +106,70 @@ public class HostHistory extends HistoryEntry {
if (hostBase != null && hostBase.getHostName() == null) {
hostBase = null;
}
// Fill in the full, symmetric, parent repo ID key
Key<HostResource> parentKey = Key.create(HostResource.class, (String) hostRepoId.getSqlKey());
parent = parentKey;
hostRepoId = VKey.create(HostResource.class, hostRepoId.getSqlKey(), parentKey);
}
// In Datastore, save as a HistoryEntry object regardless of this object's type
@Override
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
return ImmutableList.of(asHistoryEntry());
}
/** Class to represent the composite primary key of {@link HostHistory} entity. */
static class HostHistoryId extends ImmutableObject implements Serializable {
private String hostRepoId;
private Long id;
/** Hibernate requires this default constructor. */
private HostHistoryId() {}
HostHistoryId(String hostRepoId, long id) {
this.hostRepoId = hostRepoId;
this.id = id;
}
/**
* Returns the host repository id.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private String getHostRepoId() {
return hostRepoId;
}
/**
* Returns the history revision id.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private long getId() {
return id;
}
/**
* Sets the host repository id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setHostRepoId(String hostRepoId) {
this.hostRepoId = hostRepoId;
}
/**
* Sets the history revision id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setId(long id) {
this.id = id;
}
}
@Override
@@ -111,18 +190,8 @@ public class HostHistory extends HistoryEntry {
return this;
}
public Builder setHostRepoId(VKey<HostResource> hostRepoId) {
getInstance().hostRepoId = hostRepoId;
hostRepoId.maybeGetOfyKey().ifPresent(parent -> getInstance().parent = parent);
return this;
}
// We can remove this once all HistoryEntries are converted to History objects
@Override
public Builder setParent(Key<? extends EppResource> parent) {
super.setParent(parent);
getInstance().hostRepoId =
VKey.create(HostResource.class, parent.getName(), (Key<HostResource>) parent);
public Builder setHostRepoId(String hostRepoId) {
getInstance().parent = Key.create(HostResource.class, hostRepoId);
return this;
}
}
@@ -27,14 +27,18 @@ import com.googlecode.objectify.annotation.EntitySubclass;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.OnLoad;
import com.googlecode.objectify.annotation.Parent;
import google.registry.model.Buildable;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.annotations.ExternalMessagingName;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainRenewData;
import google.registry.model.eppoutput.EppResponse.ResponseData;
import google.registry.model.host.HostResource;
import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse;
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
import google.registry.model.poll.PendingActionNotificationResponse.HostPendingActionNotificationResponse;
@@ -54,10 +58,9 @@ import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Embedded;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.PostLoad;
import javax.persistence.Transient;
import org.joda.time.DateTime;
@@ -98,7 +101,6 @@ public abstract class PollMessage extends ImmutableObject
/** Entity id. */
@Id
@javax.persistence.Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "poll_message_id")
Long id;
@@ -124,11 +126,11 @@ public abstract class PollMessage extends ImmutableObject
@Ignore String hostRepoId;
@Ignore Long domainRevisionId;
@Ignore Long domainHistoryRevisionId;
@Ignore Long contactRevisionId;
@Ignore Long contactHistoryRevisionId;
@Ignore Long hostRevisionId;
@Ignore Long hostHistoryRevisionId;
public Key<HistoryEntry> getParentKey() {
return parent;
@@ -152,6 +154,34 @@ public abstract class PollMessage extends ImmutableObject
public abstract ImmutableList<ResponseData> getResponseData();
@PostLoad
void postLoad() {
if (domainRepoId != null) {
parent =
Key.create(
Key.create(DomainBase.class, domainRepoId),
HistoryEntry.class,
domainHistoryRevisionId);
} else if (contactRepoId != null) {
parent =
Key.create(
Key.create(ContactResource.class, contactRepoId),
HistoryEntry.class,
contactHistoryRevisionId);
} else if (hostHistoryRevisionId != null) {
parent =
Key.create(
Key.create(HostResource.class, hostRepoId),
HistoryEntry.class,
hostHistoryRevisionId);
}
}
@OnLoad
void onLoad() {
setSqlForeignKeys(this);
}
@Override
public abstract VKey<? extends PollMessage> createVKey();
@@ -217,10 +247,30 @@ public abstract class PollMessage extends ImmutableObject
checkArgumentNotNull(instance.clientId, "clientId must be specified");
checkArgumentNotNull(instance.eventTime, "eventTime must be specified");
checkArgumentNotNull(instance.parent, "parent must be specified");
checkArgumentNotNull(instance.parent.getParent(), "parent.getParent() must be specified");
setSqlForeignKeys(instance);
return super.build();
}
}
private static void setSqlForeignKeys(PollMessage pollMessage) {
String grandparentKind = pollMessage.parent.getParent().getKind();
String repoId = pollMessage.parent.getParent().getName();
long historyRevisionId = pollMessage.parent.getId();
if (Key.getKind(DomainBase.class).equals(grandparentKind)) {
pollMessage.domainRepoId = repoId;
pollMessage.domainHistoryRevisionId = historyRevisionId;
} else if (Key.getKind(ContactResource.class).equals(grandparentKind)) {
pollMessage.contactRepoId = repoId;
pollMessage.contactHistoryRevisionId = historyRevisionId;
} else if (Key.getKind(HostResource.class).equals(grandparentKind)) {
pollMessage.hostRepoId = repoId;
pollMessage.hostHistoryRevisionId = historyRevisionId;
} else {
throw new IllegalArgumentException("Unknown grandparent kind: " + grandparentKind);
}
}
/**
* A one-time poll message.
*
@@ -62,6 +62,7 @@ import google.registry.model.domain.fee.BaseFee.FeeType;
import google.registry.model.domain.fee.Fee;
import google.registry.model.registry.label.PremiumList;
import google.registry.model.registry.label.ReservedList;
import google.registry.schema.replay.DatastoreAndSqlEntity;
import google.registry.util.Idn;
import java.util.Map;
import java.util.Optional;
@@ -86,7 +87,7 @@ import org.joda.time.Duration;
@ReportedOn
@Entity
@javax.persistence.Entity(name = "Tld")
public class Registry extends ImmutableObject implements Buildable {
public class Registry extends ImmutableObject implements Buildable, DatastoreAndSqlEntity {
@Parent @Transient Key<EntityGroupRoot> parent = getCrossTldKey();
@@ -22,6 +22,7 @@ import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.schema.replay.DatastoreAndSqlEntity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
@@ -43,7 +44,8 @@ import org.joda.time.DateTime;
*/
@Embed
@Entity
public class DomainTransactionRecord extends ImmutableObject implements Buildable {
public class DomainTransactionRecord extends ImmutableObject
implements Buildable, DatastoreAndSqlEntity {
@Id
@Ignore
@@ -62,7 +62,7 @@ import org.joda.time.DateTime;
@MappedSuperclass
@WithStringVKey // TODO(b/162229294): This should be resolved during the course of that bug
@Access(AccessType.FIELD)
public class HistoryEntry extends ImmutableObject implements Buildable, DatastoreEntity, SqlEntity {
public class HistoryEntry extends ImmutableObject implements Buildable, DatastoreEntity {
/** Represents the type of history entry. */
public enum Type {
@@ -308,19 +308,10 @@ public class HistoryEntry extends ImmutableObject implements Buildable, Datastor
new DomainHistory.Builder().copyFrom(this).setDomainRepoId(parent.getName()).build();
} else if (parentKind.equals(getKind(HostResource.class))) {
resultEntity =
new HostHistory.Builder()
.copyFrom(this)
.setHostRepoId(
VKey.create(HostResource.class, parent.getName(), (Key<HostResource>) parent))
.build();
new HostHistory.Builder().copyFrom(this).setHostRepoId(parent.getName()).build();
} else if (parentKind.equals(getKind(ContactResource.class))) {
resultEntity =
new ContactHistory.Builder()
.copyFrom(this)
.setContactRepoId(
VKey.create(
ContactResource.class, parent.getName(), (Key<ContactResource>) parent))
.build();
new ContactHistory.Builder().copyFrom(this).setContactRepoId(parent.getName()).build();
} else {
throw new IllegalStateException(
String.format("Unknown kind of HistoryEntry parent %s", parentKind));
@@ -331,13 +322,7 @@ public class HistoryEntry extends ImmutableObject implements Buildable, Datastor
// In SQL, save the child type
@Override
public ImmutableList<SqlEntity> toSqlEntities() {
return ImmutableList.of(toChildHistoryEntity());
}
// In Datastore, save as a HistoryEntry object regardless of this object's type
@Override
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
return ImmutableList.of(asHistoryEntry());
return ImmutableList.of((SqlEntity) toChildHistoryEntity());
}
/** A builder for {@link HistoryEntry} since it is immutable */
@@ -46,6 +46,7 @@ import google.registry.model.annotations.NotBackedUp;
import google.registry.model.annotations.NotBackedUp.Reason;
import google.registry.model.annotations.VirtualEntity;
import google.registry.model.common.CrossTldSingleton;
import google.registry.schema.replay.DatastoreAndSqlEntity;
import google.registry.schema.replay.DatastoreEntity;
import google.registry.schema.replay.SqlEntity;
import google.registry.util.CollectionUtils;
@@ -97,7 +98,7 @@ import org.joda.time.DateTime;
@NotBackedUp(reason = Reason.EXTERNALLY_SOURCED)
@javax.persistence.Entity(name = "ClaimsList")
@Table
public class ClaimsListShard extends ImmutableObject implements DatastoreEntity {
public class ClaimsListShard extends ImmutableObject implements DatastoreAndSqlEntity {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -354,11 +355,6 @@ public class ClaimsListShard extends ImmutableObject implements DatastoreEntity
}
}
@Override
public ImmutableList<SqlEntity> toSqlEntities() {
return ImmutableList.of(); // ClaimsLists are dually written
}
/** Virtual parent entity for claims list shards of a specific revision. */
@Entity
@VirtualEntity
@@ -88,7 +88,7 @@ public class VKey<T> extends ImmutableObject implements Serializable {
*/
public static <T> VKey<T> create(Class<T> kind, long id) {
checkArgument(
kind.isAssignableFrom(BackupGroupRoot.class),
BackupGroupRoot.class.isAssignableFrom(kind),
"The kind %s is not a BackupGroupRoot and thus needs its entire entity group chain"
+ " specified in a parent",
kind.getCanonicalName());
@@ -106,13 +106,90 @@ public class VKey<T> extends ImmutableObject implements Serializable {
*/
public static <T> VKey<T> create(Class<T> kind, String name) {
checkArgument(
kind.isAssignableFrom(BackupGroupRoot.class),
BackupGroupRoot.class.isAssignableFrom(kind),
"The kind %s is not a BackupGroupRoot and thus needs its entire entity group chain"
+ " specified in a parent",
kind.getCanonicalName());
return new VKey<T>(kind, Key.create(kind, name), name);
}
/**
* Returns a clone with an ofy key restored from {@code ancestors}.
*
* <p>The arguments should generally consist of pairs of Class and value, where the Class is the
* kind of the ancestor key and the value is either a String or a Long.
*
* <p>For example, to restore the objectify key for
* DomainBase("COM-1234")/HistoryEntry(123)/PollEvent(567), one might use:
*
* <pre>{@code
* pollEvent.restoreOfy(DomainBase.class, "COM-1234", HistoryEntry.class, 567)
* }</pre>
*
* <p>The final key id or name is obtained from the SQL key. It is assumed that this value must be
* either a long integer or a {@code String} and that this proper identifier for the objectify
* key.
*
* <p>As a special case, an objectify Key may be used as the first ancestor instead of a Class,
* value pair.
*/
public VKey<T> restoreOfy(Object... ancestors) {
Class lastClass = null;
Key<?> lastKey = null;
for (Object ancestor : ancestors) {
if (ancestor instanceof Class) {
if (lastClass != null) {
throw new IllegalArgumentException(ancestor + " used as a key value.");
}
lastClass = (Class) ancestor;
continue;
} else if (ancestor instanceof Key) {
if (lastKey != null) {
throw new IllegalArgumentException(
"Objectify keys may only be used for the first argument");
}
lastKey = (Key) ancestor;
continue;
}
// The argument should be a value.
if (lastClass == null) {
throw new IllegalArgumentException("Argument " + ancestor + " should be a class.");
}
if (ancestor instanceof Long) {
lastKey = Key.create(lastKey, lastClass, (Long) ancestor);
} else if (ancestor instanceof String) {
lastKey = Key.create(lastKey, lastClass, (String) ancestor);
} else {
throw new IllegalArgumentException("Key value " + ancestor + " must be a string or long.");
}
lastClass = null;
}
// Make sure we didn't end up with a dangling class with no value.
if (lastClass != null) {
throw new IllegalArgumentException("Missing value for last key of type " + lastClass);
}
Object sqlKey = getSqlKey();
Key<T> ofyKey =
sqlKey instanceof Long
? Key.create(lastKey, getKind(), (Long) sqlKey)
: Key.create(lastKey, getKind(), (String) sqlKey);
return VKey.create((Class<T>) getKind(), sqlKey, ofyKey);
}
/**
* Returns a clone of {@code key} with an ofy key restored from {@code ancestors}.
*
* <p>This is the static form of the method restoreOfy() above. If {@code key} is null, it returns
* null.
*/
public static <T> VKey<T> restoreOfyFrom(@Nullable VKey<T> key, Object... ancestors) {
return key == null ? null : key.restoreOfy(ancestors);
}
/** Returns the type of the entity. */
public Class<? extends T> getKind() {
return this.kind;
-5
View File
@@ -11,11 +11,6 @@
</attributes>
</embeddable>
<sequence-generator name="HistorySequenceGenerator" sequence-name="history_id_sequence"/>
<!-- TODO(shicong): Drop this sequence and change all history tables to use the above one. -->
<sequence-generator name="TempHistorySequenceGenerator" sequence-name="temp_history_id_sequence"/>
<persistence-unit-metadata>
<persistence-unit-defaults>
<entity-listeners>
@@ -94,7 +94,10 @@
<class>google.registry.persistence.converter.ZonedDateTimeConverter</class>
<!-- Generated converters for VKey -->
<class>google.registry.model.billing.VKeyConverter_BillingEvent</class>
<class>google.registry.model.billing.VKeyConverter_Cancellation</class>
<class>google.registry.model.billing.VKeyConverter_Modification</class>
<class>google.registry.model.billing.VKeyConverter_OneTime</class>
<class>google.registry.model.billing.VKeyConverter_Recurring</class>
<class>google.registry.model.contact.VKeyConverter_ContactResource</class>
<class>google.registry.model.domain.VKeyConverter_DomainBase</class>
<class>google.registry.model.domain.token.VKeyConverter_AllocationToken</class>
@@ -273,8 +273,11 @@ class InitSqlPipelineTest {
"revisions",
"updateTimestamp",
"autorenewBillingEvent",
"autorenewBillingEventHistoryId",
"autorenewPollMessage",
"autorenewPollMessageHistoryId",
"deletePollMessage",
"deletePollMessageHistoryId",
"nsHosts",
"transferData");
assertThat(actual.getAdminContact().getSqlKey())
@@ -821,10 +821,12 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
setEppInput("domain_create_dsdata_no_maxsiglife.xml");
persistContactsAndHosts("tld"); // For some reason this sample uses "tld".
doSuccessfulTest("tld");
DomainBase domain = reloadResourceByForeignKey();
assertAboutDomains()
.that(reloadResourceByForeignKey())
.that(domain)
.hasExactlyDsData(
DelegationSignerData.create(12345, 3, 1, base16().decode("49FD46E6C4B45C55D4AC")));
DelegationSignerData.create(12345, 3, 1, base16().decode("49FD46E6C4B45C55D4AC"))
.cloneWithDomainRepoId(domain.getRepoId()));
}
@Test
@@ -14,6 +14,7 @@
package google.registry.flows.domain;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Sets.union;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
@@ -470,7 +471,11 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
.that(resource)
.hasOnlyOneHistoryEntryWhich()
.hasType(HistoryEntry.Type.DOMAIN_UPDATE);
assertThat(resource.getDsData()).isEqualTo(expectedDsData);
assertThat(resource.getDsData())
.isEqualTo(
expectedDsData.stream()
.map(ds -> ds.cloneWithDomainRepoId(resource.getRepoId()))
.collect(toImmutableSet()));
assertDnsTasksEnqueued("example.tld");
}
@@ -39,7 +39,6 @@ import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.TokenStatus;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
import google.registry.util.DateTimeUtils;
import org.joda.money.Money;
import org.joda.time.DateTime;
@@ -57,7 +56,6 @@ public class BillingEventTest extends EntityTestCase {
private HistoryEntry historyEntry;
private HistoryEntry historyEntry2;
private DomainBase domain;
private BillingEvent.OneTime sqlOneTime;
private BillingEvent.OneTime oneTime;
private BillingEvent.OneTime oneTimeSynthetic;
private BillingEvent.Recurring recurring;
@@ -107,16 +105,6 @@ public class BillingEventTest extends EntityTestCase {
.setBillingTime(now.plusDays(5))
.setAllocationToken(allocationToken.createVKey())));
sqlOneTime =
oneTime
.asBuilder()
.setDomainRepoId(domain.getRepoId())
.setDomainHistoryRevisionId(1L)
.setAllocationToken(
VKey.create(
AllocationToken.class, allocationToken.getToken(), Key.create(allocationToken)))
.build();
recurring =
persistResource(
commonInit(
@@ -179,80 +167,43 @@ public class BillingEventTest extends EntityTestCase {
}
private void saveNewBillingEvent(BillingEvent billingEvent) {
billingEvent.id = null;
jpaTm().transact(() -> jpaTm().insert(billingEvent));
}
@Test
void testCloudSqlPersistence_OneTime() {
saveRegistrar("a registrar");
saveNewBillingEvent(sqlOneTime);
saveNewBillingEvent(oneTime);
BillingEvent.OneTime persisted =
jpaTm()
.transact(
() -> jpaTm().load(VKey.createSql(BillingEvent.OneTime.class, sqlOneTime.id)));
// TODO(shicong): Remove these fixes after the entities are fully compatible
BillingEvent.OneTime persisted = jpaTm().transact(() -> jpaTm().load(oneTime.createVKey()));
// TODO(b/168325240): Remove this fix after VKeyConverter generates symmetric key for
// AllocationToken.
BillingEvent.OneTime fixed =
persisted
.asBuilder()
.setParent(sqlOneTime.getParentKey())
.setAllocationToken(sqlOneTime.getAllocationToken().get())
.build();
assertThat(fixed).isEqualTo(sqlOneTime);
persisted.asBuilder().setAllocationToken(oneTime.getAllocationToken().get()).build();
assertThat(fixed).isEqualTo(oneTime);
}
@Test
void testCloudSqlPersistence_Cancellation() {
saveRegistrar("a registrar");
saveNewBillingEvent(sqlOneTime);
VKey<BillingEvent.OneTime> sqlVKey = VKey.createSql(BillingEvent.OneTime.class, sqlOneTime.id);
BillingEvent sqlCancellationOneTime =
cancellationOneTime
.asBuilder()
.setOneTimeEventKey(sqlVKey)
.setDomainRepoId(domain.getRepoId())
.setDomainHistoryRevisionId(1L)
.build();
saveNewBillingEvent(sqlCancellationOneTime);
saveNewBillingEvent(oneTime);
saveNewBillingEvent(cancellationOneTime);
BillingEvent.Cancellation persisted =
jpaTm()
.transact(
() ->
jpaTm()
.load(
VKey.createSql(
BillingEvent.Cancellation.class, sqlCancellationOneTime.id)));
// TODO(shicong): Remove these fixes after the entities are fully compatible
jpaTm().transact(() -> jpaTm().load(cancellationOneTime.createVKey()));
// TODO(b/168537779): Remove this fix after VKey<OneTime> can be reconstructed correctly.
BillingEvent.Cancellation fixed =
persisted
.asBuilder()
.setParent(sqlCancellationOneTime.getParentKey())
.setOneTimeEventKey(sqlVKey)
.build();
assertThat(fixed).isEqualTo(sqlCancellationOneTime);
persisted.asBuilder().setOneTimeEventKey(oneTime.createVKey()).build();
assertThat(fixed).isEqualTo(cancellationOneTime);
}
@Test
void testCloudSqlPersistence_Recurring() {
saveRegistrar("a registrar");
BillingEvent.Recurring sqlRecurring =
recurring
.asBuilder()
.setDomainRepoId(domain.getRepoId())
.setDomainHistoryRevisionId(1L)
.build();
saveNewBillingEvent(sqlRecurring);
saveNewBillingEvent(recurring);
BillingEvent.Recurring persisted =
jpaTm()
.transact(
() -> jpaTm().load(VKey.createSql(BillingEvent.Recurring.class, sqlRecurring.id)));
// TODO(shicong): Remove these fixes after the entities are fully compatible
BillingEvent.Recurring fixed =
persisted.asBuilder().setParent(sqlRecurring.getParentKey()).build();
assertThat(fixed).isEqualTo(sqlRecurring);
BillingEvent.Recurring persisted = jpaTm().transact(() -> jpaTm().load(recurring.createVKey()));
assertThat(persisted).isEqualTo(recurring);
}
@Test
@@ -25,6 +25,12 @@ import static org.joda.time.DateTimeZone.UTC;
import static org.junit.jupiter.api.Assertions.fail;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.googlecode.objectify.Key;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.common.EntityGroupRoot;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact.Type;
import google.registry.model.domain.launch.LaunchNotice;
@@ -33,6 +39,8 @@ import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.HostResource;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.ContactTransferData;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTestRules;
@@ -60,6 +68,7 @@ public class DomainBaseSqlTest {
new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
private DomainBase domain;
private DomainHistory historyEntry;
private VKey<ContactResource> contactKey;
private VKey<ContactResource> contact2Key;
private VKey<HostResource> host1VKey;
@@ -334,6 +343,50 @@ public class DomainBaseSqlTest {
});
}
@Test
void testModifyDsData_addThenRemoveSuccessfully() {
persistDomain();
DelegationSignerData extraDsData =
DelegationSignerData.create(2, 2, 3, new byte[] {0, 1, 2}, "4-COM");
ImmutableSet<DelegationSignerData> unionDsData =
Sets.union(domain.getDsData(), ImmutableSet.of(extraDsData)).immutableCopy();
// Add an extra DelegationSignerData to dsData set.
jpaTm()
.transact(
() -> {
DomainBase persisted = jpaTm().load(domain.createVKey());
assertThat(persisted.getDsData()).containsExactlyElementsIn(domain.getDsData());
DomainBase modified = persisted.asBuilder().setDsData(unionDsData).build();
jpaTm().put(modified);
});
// Verify that the persisted domain entity contains both DelegationSignerData records.
jpaTm()
.transact(
() -> {
DomainBase persisted = jpaTm().load(domain.createVKey());
assertThat(persisted.getDsData()).containsExactlyElementsIn(unionDsData);
assertEqualDomainExcept(persisted, "dsData");
});
// Remove the extra DelegationSignerData record from dsData set.
jpaTm()
.transact(
() -> {
DomainBase persisted = jpaTm().load(domain.createVKey());
jpaTm().put(persisted.asBuilder().setDsData(domain.getDsData()).build());
});
// Verify that the persisted domain is equal to the original domain.
jpaTm()
.transact(
() -> {
DomainBase persisted = jpaTm().load(domain.createVKey());
assertEqualDomainExcept(persisted);
});
}
@Test
void testUpdates() {
jpaTm()
@@ -350,16 +403,6 @@ public class DomainBaseSqlTest {
.transact(
() -> {
DomainBase result = jpaTm().load(domain.createVKey());
// Fix DS data, since we can't persist that yet.
result =
result
.asBuilder()
.setDsData(
ImmutableSet.of(
DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2})))
.build();
assertAboutImmutableObjects()
.that(result)
.isEqualExceptFields(domain, "updateTimestamp", "creationTime");
@@ -395,14 +438,175 @@ public class DomainBaseSqlTest {
});
}
private void assertEqualDomainExcept(DomainBase thatDomain, String... excepts) {
// Fix DS data, since we can't persist it yet.
thatDomain =
thatDomain
.asBuilder()
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2})))
.build();
@Test
void persistDomainWithCompositeVKeys() {
jpaTm()
.transact(
() -> {
historyEntry =
new DomainHistory.Builder()
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setPeriod(Period.create(1, Period.Unit.YEARS))
.setModificationTime(DateTime.now(UTC))
.setParent(Key.create(DomainBase.class, "4-COM"))
.setDomainRepoId("4-COM")
// These are non-null, but I don't think some tests set them.
.setReason("felt like it")
.setRequestedByRegistrar(false)
.setXmlBytes(new byte[0])
.build();
BillingEvent.Recurring billEvent =
new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId("example.com")
.setClientId("registrar1")
.setDomainRepoId("4-COM")
.setDomainHistoryRevisionId(1L)
.setEventTime(DateTime.now(UTC).plusYears(1))
.setRecurrenceEndTime(END_OF_TIME)
.setParent(historyEntry)
.build();
PollMessage.Autorenew autorenewPollMessage =
new PollMessage.Autorenew.Builder()
.setClientId("registrar1")
.setEventTime(DateTime.now(UTC).plusYears(1))
.setParent(historyEntry)
.build();
PollMessage.OneTime deletePollMessage =
new PollMessage.OneTime.Builder()
.setClientId("registrar1")
.setEventTime(DateTime.now(UTC).plusYears(1))
.setParent(historyEntry)
.build();
jpaTm().insert(contact);
jpaTm().insert(contact2);
jpaTm().insert(host);
domain =
domain
.asBuilder()
.setAutorenewBillingEvent(billEvent.createVKey())
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
.setDeletePollMessage(deletePollMessage.createVKey())
.build();
historyEntry = historyEntry.asBuilder().setDomainContent(domain).build();
jpaTm().insert(historyEntry);
jpaTm().insert(autorenewPollMessage);
jpaTm().insert(billEvent);
jpaTm().insert(deletePollMessage);
jpaTm().insert(domain);
});
// Store the existing BillingRecurrence VKey. This happens after the event has been persisted.
DomainBase persisted = jpaTm().transact(() -> jpaTm().load(domain.createVKey()));
// Verify that the domain data has been persisted.
// dsData still isn't persisted. gracePeriods appears to have the same values but for some
// reason is showing up as different.
assertEqualDomainExcept(persisted, "creationTime", "dsData", "gracePeriods");
// Verify that the DomainContent object from the history record sets the fields correctly.
DomainHistory persistedHistoryEntry =
jpaTm().transact(() -> jpaTm().load(historyEntry.createVKey()));
assertThat(persistedHistoryEntry.getDomainContent().get().getAutorenewPollMessage())
.isEqualTo(domain.getAutorenewPollMessage());
assertThat(persistedHistoryEntry.getDomainContent().get().getAutorenewBillingEvent())
.isEqualTo(domain.getAutorenewBillingEvent());
assertThat(persistedHistoryEntry.getDomainContent().get().getDeletePollMessage())
.isEqualTo(domain.getDeletePollMessage());
}
@Test
void persistDomainWithLegacyVKeys() {
jpaTm()
.transact(
() -> {
historyEntry =
new DomainHistory.Builder()
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setPeriod(Period.create(1, Period.Unit.YEARS))
.setModificationTime(DateTime.now(UTC))
.setParent(Key.create(DomainBase.class, "4-COM"))
.setDomainRepoId("4-COM")
// These are non-null, but I don't think some tests set them.
.setReason("felt like it")
.setRequestedByRegistrar(false)
.setXmlBytes(new byte[0])
.build();
BillingEvent.Recurring billEvent =
new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId("example.com")
.setClientId("registrar1")
.setDomainRepoId("4-COM")
.setDomainHistoryRevisionId(1L)
.setEventTime(DateTime.now(UTC).plusYears(1))
.setRecurrenceEndTime(END_OF_TIME)
.setParent(historyEntry)
.build();
PollMessage.Autorenew autorenewPollMessage =
new PollMessage.Autorenew.Builder()
.setClientId("registrar1")
.setEventTime(DateTime.now(UTC).plusYears(1))
.setParent(historyEntry)
.build();
PollMessage.OneTime deletePollMessage =
new PollMessage.OneTime.Builder()
.setClientId("registrar1")
.setEventTime(DateTime.now(UTC).plusYears(1))
.setParent(historyEntry)
.build();
jpaTm().insert(contact);
jpaTm().insert(contact2);
jpaTm().insert(host);
domain =
domain
.asBuilder()
.setAutorenewBillingEvent(
createLegacyVKey(BillingEvent.Recurring.class, billEvent.getId()))
.setAutorenewPollMessage(
createLegacyVKey(PollMessage.Autorenew.class, deletePollMessage.getId()))
.setDeletePollMessage(
createLegacyVKey(PollMessage.OneTime.class, autorenewPollMessage.getId()))
.build();
historyEntry = historyEntry.asBuilder().setDomainContent(domain).build();
jpaTm().insert(historyEntry);
jpaTm().insert(autorenewPollMessage);
jpaTm().insert(billEvent);
jpaTm().insert(deletePollMessage);
jpaTm().insert(domain);
});
// Store the existing BillingRecurrence VKey. This happens after the event has been persisted.
DomainBase persisted = jpaTm().transact(() -> jpaTm().load(domain.createVKey()));
// Verify that the domain data has been persisted.
// dsData still isn't persisted. gracePeriods appears to have the same values but for some
// reason is showing up as different.
assertEqualDomainExcept(persisted, "creationTime", "dsData", "gracePeriods");
// Verify that the DomainContent object from the history record sets the fields correctly.
DomainHistory persistedHistoryEntry =
jpaTm().transact(() -> jpaTm().load(historyEntry.createVKey()));
assertThat(persistedHistoryEntry.getDomainContent().get().getAutorenewPollMessage())
.isEqualTo(domain.getAutorenewPollMessage());
assertThat(persistedHistoryEntry.getDomainContent().get().getAutorenewBillingEvent())
.isEqualTo(domain.getAutorenewBillingEvent());
assertThat(persistedHistoryEntry.getDomainContent().get().getDeletePollMessage())
.isEqualTo(domain.getDeletePollMessage());
}
private <T> VKey<T> createLegacyVKey(Class<T> clazz, long id) {
return VKey.create(
clazz, id, Key.create(Key.create(EntityGroupRoot.class, "per-tld"), clazz, id));
}
private void assertEqualDomainExcept(DomainBase thatDomain, String... excepts) {
// Fix the original creation timestamp (this gets initialized on first write)
DomainBase org = domain.asBuilder().setCreationTime(thatDomain.getCreationTime()).build();
@@ -47,17 +47,14 @@ public class ContactHistoryTest extends EntityTestCase {
jpaTm().transact(() -> jpaTm().insert(contact));
VKey<ContactResource> contactVKey = contact.createVKey();
ContactResource contactFromDb = jpaTm().transact(() -> jpaTm().load(contactVKey));
ContactHistory contactHistory = createContactHistory(contactFromDb, contactVKey);
contactHistory.id = null;
ContactHistory contactHistory = createContactHistory(contactFromDb, contact.getRepoId());
jpaTm().transact(() -> jpaTm().insert(contactHistory));
jpaTm()
.transact(
() -> {
ContactHistory fromDatabase =
jpaTm().load(VKey.createSql(ContactHistory.class, contactHistory.getId()));
ContactHistory fromDatabase = jpaTm().load(contactHistory.createVKey());
assertContactHistoriesEqual(fromDatabase, contactHistory);
assertThat(fromDatabase.getContactRepoId().getSqlKey())
.isEqualTo(contactHistory.getContactRepoId().getSqlKey());
assertThat(fromDatabase.getParentVKey()).isEqualTo(contactHistory.getParentVKey());
});
}
@@ -70,18 +67,18 @@ public class ContactHistoryTest extends EntityTestCase {
VKey<ContactResource> contactVKey = contact.createVKey();
ContactResource contactFromDb = jpaTm().transact(() -> jpaTm().load(contactVKey));
ContactHistory contactHistory =
createContactHistory(contactFromDb, contactVKey).asBuilder().setContactBase(null).build();
contactHistory.id = null;
createContactHistory(contactFromDb, contact.getRepoId())
.asBuilder()
.setContactBase(null)
.build();
jpaTm().transact(() -> jpaTm().insert(contactHistory));
jpaTm()
.transact(
() -> {
ContactHistory fromDatabase =
jpaTm().load(VKey.createSql(ContactHistory.class, contactHistory.getId()));
ContactHistory fromDatabase = jpaTm().load(contactHistory.createVKey());
assertContactHistoriesEqual(fromDatabase, contactHistory);
assertThat(fromDatabase.getContactRepoId().getSqlKey())
.isEqualTo(contactHistory.getContactRepoId().getSqlKey());
assertThat(fromDatabase.getParentVKey()).isEqualTo(contactHistory.getParentVKey());
});
}
@@ -94,14 +91,13 @@ public class ContactHistoryTest extends EntityTestCase {
VKey<ContactResource> contactVKey = contact.createVKey();
ContactResource contactFromDb = tm().transact(() -> tm().load(contactVKey));
fakeClock.advanceOneMilli();
ContactHistory contactHistory = createContactHistory(contactFromDb, contactVKey);
ContactHistory contactHistory = createContactHistory(contactFromDb, contact.getRepoId());
tm().transact(() -> tm().insert(contactHistory));
// retrieving a HistoryEntry or a ContactHistory with the same key should return the same object
// note: due to the @EntitySubclass annotation. all Keys for ContactHistory objects will have
// type HistoryEntry
VKey<ContactHistory> contactHistoryVKey =
VKey.createOfy(ContactHistory.class, Key.create(contactHistory));
VKey<ContactHistory> contactHistoryVKey = contactHistory.createVKey();
VKey<HistoryEntry> historyEntryVKey =
VKey.createOfy(HistoryEntry.class, Key.create(contactHistory.asHistoryEntry()));
ContactHistory hostHistoryFromDb = tm().transact(() -> tm().load(contactHistoryVKey));
@@ -110,8 +106,7 @@ public class ContactHistoryTest extends EntityTestCase {
assertThat(hostHistoryFromDb).isEqualTo(historyEntryFromDb);
}
private ContactHistory createContactHistory(
ContactBase contact, VKey<ContactResource> contactVKey) {
private ContactHistory createContactHistory(ContactBase contact, String contactRepoId) {
return new ContactHistory.Builder()
.setType(HistoryEntry.Type.HOST_CREATE)
.setXmlBytes("<xml></xml>".getBytes(UTF_8))
@@ -122,14 +117,14 @@ public class ContactHistoryTest extends EntityTestCase {
.setReason("reason")
.setRequestedByRegistrar(true)
.setContactBase(contact)
.setContactRepoId(contactVKey)
.setContactRepoId(contactRepoId)
.build();
}
static void assertContactHistoriesEqual(ContactHistory one, ContactHistory two) {
assertAboutImmutableObjects()
.that(one)
.isEqualExceptFields(two, "contactBase", "contactRepoId", "parent");
.isEqualExceptFields(two, "contactBase", "contactRepoId");
assertAboutImmutableObjects()
.that(one.getContactBase().orElse(null))
.isEqualExceptFields(two.getContactBase().orElse(null), "repoId");
@@ -59,7 +59,6 @@ public class DomainHistoryTest extends EntityTestCase {
void testPersistence() {
DomainBase domain = createDomainWithContactsAndHosts();
DomainHistory domainHistory = createDomainHistory(domain);
domainHistory.id = null;
jpaTm().transact(() -> jpaTm().insert(domainHistory));
jpaTm()
@@ -67,8 +66,7 @@ public class DomainHistoryTest extends EntityTestCase {
() -> {
DomainHistory fromDatabase = jpaTm().load(domainHistory.createVKey());
assertDomainHistoriesEqual(fromDatabase, domainHistory);
assertThat(fromDatabase.getDomainRepoId().getSqlKey())
.isEqualTo(domainHistory.getDomainRepoId().getSqlKey());
assertThat(fromDatabase.getParentVKey()).isEqualTo(domainHistory.getParentVKey());
});
}
@@ -77,7 +75,6 @@ public class DomainHistoryTest extends EntityTestCase {
DomainBase domain = createDomainWithContactsAndHosts();
DomainHistory domainHistory =
createDomainHistory(domain).asBuilder().setDomainContent(null).build();
domainHistory.id = null;
jpaTm().transact(() -> jpaTm().insert(domainHistory));
jpaTm()
@@ -85,8 +82,7 @@ public class DomainHistoryTest extends EntityTestCase {
() -> {
DomainHistory fromDatabase = jpaTm().load(domainHistory.createVKey());
assertDomainHistoriesEqual(fromDatabase, domainHistory);
assertThat(fromDatabase.getDomainRepoId().getSqlKey())
.isEqualTo(domainHistory.getDomainRepoId().getSqlKey());
assertThat(fromDatabase.getParentVKey()).isEqualTo(domainHistory.getParentVKey());
assertThat(fromDatabase.getNsHosts())
.containsExactlyElementsIn(
domainHistory.getNsHosts().stream()
@@ -121,8 +117,7 @@ public class DomainHistoryTest extends EntityTestCase {
// retrieving a HistoryEntry or a DomainHistory with the same key should return the same object
// note: due to the @EntitySubclass annotation. all Keys for DomainHistory objects will have
// type HistoryEntry
VKey<DomainHistory> domainHistoryVKey =
VKey.createOfy(DomainHistory.class, Key.create(domainHistory));
VKey<DomainHistory> domainHistoryVKey = domainHistory.createVKey();
VKey<HistoryEntry> historyEntryVKey =
VKey.createOfy(HistoryEntry.class, Key.create(domainHistory.asHistoryEntry()));
DomainHistory domainHistoryFromDb = tm().transact(() -> tm().load(domainHistoryVKey));
@@ -155,7 +150,7 @@ public class DomainHistoryTest extends EntityTestCase {
assertAboutImmutableObjects()
.that(one)
.isEqualExceptFields(
two, "domainContent", "domainRepoId", "parent", "nsHosts", "domainTransactionRecords");
two, "domainContent", "domainRepoId", "nsHosts", "domainTransactionRecords");
assertThat(one.getDomainContent().map(DomainContent::getDomainName))
.isEqualTo(two.getDomainContent().map(DomainContent::getDomainName));
// NB: the record's ID gets reset by Hibernate, causing the hash code to differ so we have to
@@ -48,17 +48,14 @@ public class HostHistoryTest extends EntityTestCase {
VKey<HostResource> hostVKey =
VKey.create(HostResource.class, "host1", Key.create(HostResource.class, "host1"));
HostResource hostFromDb = jpaTm().transact(() -> jpaTm().load(hostVKey));
HostHistory hostHistory = createHostHistory(hostFromDb, hostVKey);
hostHistory.id = null;
HostHistory hostHistory = createHostHistory(hostFromDb, host.getRepoId());
jpaTm().transact(() -> jpaTm().insert(hostHistory));
jpaTm()
.transact(
() -> {
HostHistory fromDatabase =
jpaTm().load(VKey.createSql(HostHistory.class, hostHistory.getId()));
HostHistory fromDatabase = jpaTm().load(hostHistory.createVKey());
assertHostHistoriesEqual(fromDatabase, hostHistory);
assertThat(fromDatabase.getHostRepoId().getSqlKey())
.isEqualTo(hostHistory.getHostRepoId().getSqlKey());
assertThat(fromDatabase.getParentVKey()).isEqualTo(hostHistory.getParentVKey());
});
}
@@ -68,23 +65,17 @@ public class HostHistoryTest extends EntityTestCase {
HostResource host = newHostResourceWithRoid("ns1.example.com", "host1");
jpaTm().transact(() -> jpaTm().insert(host));
VKey<HostResource> hostVKey =
VKey.create(HostResource.class, "host1", Key.create(HostResource.class, "host1"));
HostResource hostFromDb = jpaTm().transact(() -> jpaTm().load(hostVKey));
HostResource hostFromDb = jpaTm().transact(() -> jpaTm().load(host.createVKey()));
HostHistory hostHistory =
createHostHistory(hostFromDb, hostVKey).asBuilder().setHostBase(null).build();
hostHistory.id = null;
createHostHistory(hostFromDb, host.getRepoId()).asBuilder().setHostBase(null).build();
jpaTm().transact(() -> jpaTm().insert(hostHistory));
jpaTm()
.transact(
() -> {
HostHistory fromDatabase =
jpaTm().load(VKey.createSql(HostHistory.class, hostHistory.getId()));
HostHistory fromDatabase = jpaTm().load(hostHistory.createVKey());
assertHostHistoriesEqual(fromDatabase, hostHistory);
assertThat(fromDatabase.getHostRepoId().getSqlKey())
.isEqualTo(hostHistory.getHostRepoId().getSqlKey());
assertThat(fromDatabase.getParentVKey()).isEqualTo(hostHistory.getParentVKey());
});
}
@@ -97,14 +88,14 @@ public class HostHistoryTest extends EntityTestCase {
VKey<HostResource> hostVKey =
VKey.create(HostResource.class, "host1", Key.create(HostResource.class, "host1"));
HostResource hostFromDb = tm().transact(() -> tm().load(hostVKey));
HostHistory hostHistory = createHostHistory(hostFromDb, hostVKey);
HostHistory hostHistory = createHostHistory(hostFromDb, host.getRepoId());
fakeClock.advanceOneMilli();
tm().transact(() -> tm().insert(hostHistory));
// retrieving a HistoryEntry or a HostHistory with the same key should return the same object
// note: due to the @EntitySubclass annotation. all Keys for HostHistory objects will have
// type HistoryEntry
VKey<HostHistory> hostHistoryVKey = VKey.createOfy(HostHistory.class, Key.create(hostHistory));
VKey<HostHistory> hostHistoryVKey = hostHistory.createVKey();
VKey<HistoryEntry> historyEntryVKey =
VKey.createOfy(HistoryEntry.class, Key.create(hostHistory.asHistoryEntry()));
HostHistory hostHistoryFromDb = tm().transact(() -> tm().load(hostHistoryVKey));
@@ -120,7 +111,7 @@ public class HostHistoryTest extends EntityTestCase {
.isEqualExceptFields(two.getHostBase().orElse(null), "repoId");
}
private HostHistory createHostHistory(HostBase hostBase, VKey<HostResource> hostVKey) {
private HostHistory createHostHistory(HostBase hostBase, String hostRepoId) {
return new HostHistory.Builder()
.setType(HistoryEntry.Type.HOST_CREATE)
.setXmlBytes("<xml></xml>".getBytes(UTF_8))
@@ -131,7 +122,7 @@ public class HostHistoryTest extends EntityTestCase {
.setReason("reason")
.setRequestedByRegistrar(true)
.setHostBase(hostBase)
.setHostRepoId(hostVKey)
.setHostRepoId(hostRepoId)
.build();
}
}
@@ -80,7 +80,6 @@ public class LegacyHistoryObjectTest extends EntityTestCase {
ContactHistory legacyContactHistory = (ContactHistory) fromObjectify;
// Next, save that from-Datastore object in SQL and verify we can load it back in
legacyContactHistory.id = null;
jpaTm()
.transact(
() -> {
@@ -88,17 +87,13 @@ public class LegacyHistoryObjectTest extends EntityTestCase {
jpaTm().insert(legacyContactHistory);
});
ContactHistory legacyHistoryFromSql =
jpaTm()
.transact(
() ->
jpaTm()
.load(VKey.createSql(ContactHistory.class, legacyContactHistory.getId())));
jpaTm().transact(() -> jpaTm().load(legacyContactHistory.createVKey()));
assertAboutImmutableObjects()
.that(legacyContactHistory)
.isEqualExceptFields(legacyHistoryFromSql);
// can't compare contactRepoId directly since it doesn't save the ofy key
assertThat(legacyContactHistory.getContactRepoId().getSqlKey())
.isEqualTo(legacyHistoryFromSql.getContactRepoId().getSqlKey());
assertThat(legacyContactHistory.getParentVKey().getSqlKey())
.isEqualTo(legacyHistoryFromSql.getParentVKey().getSqlKey());
}
@Test
@@ -164,7 +159,6 @@ public class LegacyHistoryObjectTest extends EntityTestCase {
HostHistory legacyHostHistory = (HostHistory) fromObjectify;
// Next, save that from-Datastore object in SQL and verify we can load it back in
legacyHostHistory.id = null;
jpaTm()
.transact(
() -> {
@@ -172,13 +166,11 @@ public class LegacyHistoryObjectTest extends EntityTestCase {
jpaTm().insert(legacyHostHistory);
});
HostHistory legacyHistoryFromSql =
jpaTm()
.transact(
() -> jpaTm().load(VKey.createSql(HostHistory.class, legacyHostHistory.getId())));
jpaTm().transact(() -> jpaTm().load(legacyHostHistory.createVKey()));
assertAboutImmutableObjects().that(legacyHostHistory).isEqualExceptFields(legacyHistoryFromSql);
// can't compare hostRepoId directly since it doesn't save the ofy key in SQL
assertThat(legacyHostHistory.getHostRepoId().getSqlKey())
.isEqualTo(legacyHistoryFromSql.getHostRepoId().getSqlKey());
assertThat(legacyHostHistory.getParentVKey().getSqlKey())
.isEqualTo(legacyHistoryFromSql.getParentVKey().getSqlKey());
}
private HistoryEntry historyEntryForDomain(DomainBase domain) {
@@ -18,12 +18,15 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
import static google.registry.testing.DatastoreHelper.newDomainBase;
import static google.registry.testing.DatastoreHelper.persistActiveContact;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.SqlHelper.saveRegistrar;
import static java.nio.charset.StandardCharsets.UTF_8;
import google.registry.model.EntityTestCase;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.Period;
import google.registry.model.eppcommon.Trid;
import google.registry.model.reporting.HistoryEntry;
@@ -34,6 +37,7 @@ import org.junit.jupiter.api.Test;
/** Unit tests for {@link PollMessage}. */
public class PollMessageTest extends EntityTestCase {
private DomainBase domain;
private HistoryEntry historyEntry;
private PollMessage.OneTime oneTime;
private PollMessage.Autorenew autoRenew;
@@ -45,15 +49,17 @@ public class PollMessageTest extends EntityTestCase {
@BeforeEach
void setUp() {
createTld("foobar");
ContactResource contact = persistActiveContact("contact1234");
domain = persistResource(newDomainBase("foo.foobar", contact));
historyEntry =
persistResource(
new HistoryEntry.Builder()
.setParent(persistActiveDomain("foo.foobar"))
.setParent(domain)
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setPeriod(Period.create(1, Period.Unit.YEARS))
.setXmlBytes("<xml></xml>".getBytes(UTF_8))
.setModificationTime(fakeClock.nowUtc())
.setClientId("foo")
.setClientId("TheRegistrar")
.setTrid(Trid.create("ABC-123", "server-trid"))
.setBySuperuser(false)
.setReason("reason")
@@ -61,6 +67,7 @@ public class PollMessageTest extends EntityTestCase {
.build());
oneTime =
new PollMessage.OneTime.Builder()
.setId(100L)
.setClientId("TheRegistrar")
.setEventTime(fakeClock.nowUtc())
.setMsg("Test poll message")
@@ -68,6 +75,7 @@ public class PollMessageTest extends EntityTestCase {
.build();
autoRenew =
new PollMessage.Autorenew.Builder()
.setId(200L)
.setClientId("TheRegistrar")
.setEventTime(fakeClock.nowUtc())
.setMsg("Test poll message")
@@ -75,50 +83,46 @@ public class PollMessageTest extends EntityTestCase {
.setAutorenewEndTime(fakeClock.nowUtc().plusDays(365))
.setTargetId("foobar.foo")
.build();
// TODO(shicong): Remove these two lines after we use symmetric vkey and change the cloud sql
// schema
oneTime.id = null;
autoRenew.id = null;
jpaTm()
.transact(
() -> {
saveRegistrar("TheRegistrar");
jpaTm().insert(contact);
jpaTm().insert(domain);
jpaTm().insert(historyEntry.toChildHistoryEntity());
});
}
@Test
void testCloudSqlPersistenceOneTime() {
saveRegistrar("TheRegistrar");
jpaTm().transact(() -> jpaTm().insert(oneTime));
PollMessage.OneTime persisted =
jpaTm().transact(() -> jpaTm().load(VKey.createSql(PollMessage.OneTime.class, oneTime.id)));
persisted.parent = oneTime.parent;
assertThat(persisted).isEqualTo(oneTime);
}
@Test
void testCloudSqlPersistenceAutorenew() {
saveRegistrar("TheRegistrar");
jpaTm().transact(() -> jpaTm().insert(autoRenew));
PollMessage.Autorenew persisted =
jpaTm()
.transact(
() -> jpaTm().load(VKey.createSql(PollMessage.Autorenew.class, autoRenew.id)));
persisted.parent = autoRenew.parent;
assertThat(persisted).isEqualTo(autoRenew);
}
@Test
void testCloudSqlSupportForPolymorphicVKey() {
saveRegistrar("TheRegistrar");
jpaTm().transact(() -> jpaTm().insert(oneTime));
PollMessage persistedOneTime =
jpaTm().transact(() -> jpaTm().load(VKey.createSql(PollMessage.class, oneTime.getId())));
assertThat(persistedOneTime).isInstanceOf(PollMessage.OneTime.class);
persistedOneTime.parent = oneTime.parent;
assertThat(persistedOneTime).isEqualTo(oneTime);
jpaTm().transact(() -> jpaTm().insert(autoRenew));
PollMessage persistedAutoRenew =
jpaTm().transact(() -> jpaTm().load(VKey.createSql(PollMessage.class, autoRenew.getId())));
assertThat(persistedAutoRenew).isInstanceOf(PollMessage.Autorenew.class);
persistedAutoRenew.parent = oneTime.parent;
assertThat(persistedAutoRenew).isEqualTo(autoRenew);
}
@@ -18,6 +18,7 @@ import static com.google.common.truth.Truth8.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import google.registry.model.billing.BillingEvent.OneTime;
import google.registry.model.registrar.RegistrarContact;
import google.registry.testing.AppEngineExtension;
@@ -59,4 +60,60 @@ class VKeyTest {
() -> VKey.create(RegistrarContact.class, "fake@example.com"));
assertThat(thrown).hasMessageThat().contains("BackupGroupRoot");
}
@Test
void testRestoreOfy() {
assertThat(VKey.restoreOfyFrom(null, TestObject.class, 100)).isNull();
VKey<TestObject> key = VKey.createSql(TestObject.class, "foo");
VKey<TestObject> restored = key.restoreOfy(TestObject.class, "bar");
assertThat(restored.getOfyKey())
.isEqualTo(Key.create(Key.create(TestObject.class, "bar"), TestObject.class, "foo"));
assertThat(restored.getSqlKey()).isEqualTo("foo");
assertThat(VKey.restoreOfyFrom(key).getOfyKey()).isEqualTo(Key.create(TestObject.class, "foo"));
restored = key.restoreOfy(OtherObject.class, "baz", TestObject.class, "bar");
assertThat(restored.getOfyKey())
.isEqualTo(
Key.create(
Key.create(Key.create(OtherObject.class, "baz"), TestObject.class, "bar"),
TestObject.class,
"foo"));
// Verify that we can use a key as the first argument.
restored = key.restoreOfy(Key.create(TestObject.class, "bar"));
assertThat(restored.getOfyKey())
.isEqualTo(Key.create(Key.create(TestObject.class, "bar"), TestObject.class, "foo"));
// Verify that we get an exception when a key is not the first argument.
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> key.restoreOfy(TestObject.class, "foo", Key.create(TestObject.class, "bar")));
assertThat(thrown)
.hasMessageThat()
.contains("Objectify keys may only be used for the first argument");
// Verify other exception cases.
thrown =
assertThrows(
IllegalArgumentException.class,
() -> key.restoreOfy(TestObject.class, TestObject.class));
assertThat(thrown)
.hasMessageThat()
.contains("class google.registry.testing.TestObject used as a key value.");
thrown =
assertThrows(IllegalArgumentException.class, () -> key.restoreOfy(TestObject.class, 1.5));
assertThat(thrown).hasMessageThat().contains("Key value 1.5 must be a string or long.");
thrown = assertThrows(IllegalArgumentException.class, () -> key.restoreOfy(TestObject.class));
assertThat(thrown)
.hasMessageThat()
.contains("Missing value for last key of type class google.registry.testing.TestObject");
}
@Entity
static class OtherObject {}
}
@@ -18,6 +18,7 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.annotation.Embed;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ClassInfo;
import io.github.classgraph.ClassInfoList;
@@ -72,6 +73,7 @@ public class EntityTest {
.filter(ClassInfo::isStandardClass)
.map(ClassInfo::loadClass)
.filter(clazz -> !clazz.isAnnotationPresent(EntityForTesting.class))
.filter(clazz -> !clazz.isAnnotationPresent(Embed.class))
.map(Class::getName)
.collect(toImmutableSet());
}
@@ -14,7 +14,7 @@
package google.registry.ui.server.registrar;
import static com.google.appengine.repackaged.com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.monitoring.metrics.contrib.LongMetricSubject.assertThat;
import static google.registry.config.RegistryConfig.getGSuiteOutgoingEmailAddress;
import static google.registry.config.RegistryConfig.getGSuiteOutgoingEmailDisplayName;
@@ -55,7 +55,7 @@ FROM (
FROM
`my-project-id.latest_datastore_export.Registry`
WHERE
enableInvoicing IS TRUE) ) AS BillingEvent
invoicingEnabled IS TRUE) ) AS BillingEvent
-- Gather billing ID from registrar table
-- This is a 'JOIN' as opposed to 'LEFT JOIN' to filter out
-- non-billable registrars
@@ -41,7 +41,7 @@ class google.registry.model.billing.BillingEvent$OneTime {
@Id java.lang.Long id;
@Parent com.googlecode.objectify.Key<google.registry.model.reporting.HistoryEntry> parent;
google.registry.model.billing.BillingEvent$Reason reason;
google.registry.persistence.VKey<? extends google.registry.model.billing.BillingEvent> cancellationMatchingBillingEvent;
google.registry.persistence.VKey<google.registry.model.billing.BillingEvent$Recurring> cancellationMatchingBillingEvent;
google.registry.persistence.VKey<google.registry.model.domain.token.AllocationToken> allocationToken;
java.lang.Integer periodYears;
java.lang.String clientId;
@@ -131,7 +131,6 @@ class google.registry.model.contact.ContactHistory {
google.registry.model.domain.Period period;
google.registry.model.eppcommon.Trid trid;
google.registry.model.reporting.HistoryEntry$Type type;
google.registry.persistence.VKey<google.registry.model.contact.ContactResource> contactRepoId;
java.lang.Boolean requestedByRegistrar;
java.lang.String clientId;
java.lang.String otherClientId;
@@ -272,7 +271,6 @@ class google.registry.model.domain.DomainHistory {
google.registry.model.reporting.HistoryEntry$Type type;
java.lang.Boolean requestedByRegistrar;
java.lang.String clientId;
java.lang.String domainRepoId;
java.lang.String otherClientId;
java.lang.String reason;
java.util.Set<google.registry.model.reporting.DomainTransactionRecord> domainTransactionRecords;
@@ -401,7 +399,6 @@ class google.registry.model.host.HostHistory {
google.registry.model.eppcommon.Trid trid;
google.registry.model.host.HostBase hostBase;
google.registry.model.reporting.HistoryEntry$Type type;
google.registry.persistence.VKey<google.registry.model.host.HostResource> hostRepoId;
java.lang.Boolean requestedByRegistrar;
java.lang.String clientId;
java.lang.String otherClientId;
+5
View File
@@ -61,6 +61,11 @@ Below are the steps to submit a schema change:
You'll want to have a look at the diffs in the golden schema to verify that
all changes are intentional.
5. Run ./nom_build :db:generateFlywayIndex to regenerate the Flyway index.
This is a file listing all of the current Flyway files. Its purpose is to
produce a merge conflict when more than one person adds a Flyway file with
the same sequence number.
Relevant files (under db/src/main/resources/sql/schema/):
* nomulus.golden.sql is the schema dump (pg_dump for postgres) of the final
+57 -1
View File
@@ -59,8 +59,11 @@ ext {
getJdbcAccessInfo = {
if (allDbEnv.contains(dbServer)) {
return getSocketFactoryAccessInfo(dbServer)
} else {
} else if (!dbServer.isEmpty()) {
return getAccessInfoByHostPort(dbServer)
} else {
// Not connecting to a database. Return a dummy object for Flyway config.
return [ url: '', user: '', password: '' ]
}
}
@@ -110,6 +113,7 @@ task compileApiJar(type: Jar) {
configurations {
compileApi
schema
integration
}
artifacts {
@@ -166,6 +170,28 @@ dependencies {
testCompile project(path: ':common', configuration: 'testing')
}
task generateFlywayIndex {
def flywayBase = "$projectDir/src/main/resources/sql/flyway"
def filenamePattern = /V(\d+)__.*\.sql/
def getSeqNum = { file ->
def match = file.getName() =~ filenamePattern
if (match.size() != 1) {
throw new IllegalArgumentException("Bad Flyway filename: $file")
}
return match[0][1] as int
}
doLast {
def files = new File(flywayBase).listFiles()
def indexFile = new File("${flywayBase}.txt")
indexFile.write ''
for (def file : files.sort{a, b -> getSeqNum(a) <=> getSeqNum(b)}) {
indexFile << "${file.name}\n"
}
}
}
flywayInfo.dependsOn('buildNeeded')
flywayValidate.dependsOn('buildNeeded')
@@ -187,3 +213,33 @@ if (ext.isRestricted()) {
}
}
}
if (project.baseSchemaTag != '') {
repositories {
maven {
url project.publish_repo
}
}
dependencies {
integration "google.registry:schema:${project.baseSchemaTag}"
}
// Checks if Flyway scripts can be deployed to an existing database with
// an older release. Please refer to SchemaTest.java for more information.
task schemaIncrementalDeployTest(dependsOn: processResources, type: Test) {
useJUnitPlatform()
include 'google/registry/sql/flyway/SchemaTest.*'
classpath = configurations.testRuntimeClasspath
.plus(configurations.integration)
.plus(files(sourceSets.test.output.classesDirs))
.plus(files(sourceSets.test.output.resourcesDir))
.plus(files(sourceSets.main.output.classesDirs))
// Declare test-runtime dependency on Flyway scripts in the resources dir.
// They are not on classpath since they conflict with the base schema.
inputs.dir sourceSets.main.output.resourcesDir
// Specifies which test to run using the following property
systemProperty 'deploy_to_existing_db', 'true'
}
}
+63
View File
@@ -0,0 +1,63 @@
V1__create_claims_list_and_entry.sql
V2__create_premium_list_and_entry.sql
V3__create_registry_lock.sql
V4__registry_lock_add_index_on_verification_code.sql
V5__update_premium_list.sql
V6__premium_list_bloom_filter.sql
V7__update_claims_list.sql
V8__registry_lock_registrar_index.sql
V9__premium_list_currency_type.sql
V10__create_reserved_list_and_entry.sql
V11__premium_entry_reorder_column.sql
V12__create_cursor.sql
V13__refactor_registry_lock.sql
V14__load_extension_for_hstore.sql
V15__add_epp_resources.sql
V16__create_registrar.sql
V17__create_registrar_poc.sql
V18__create_lock.sql
V19__add_registry_relock_reference.sql
V20__add_relock_duration.sql
V21__add_registry_lock_email_to_poc.sql
V22__update_ns_hosts.sql
V23__create_contact.sql
V24__domain_base_contacts.sql
V25__rename_vkey_fields.sql
V26__create_billing_event.sql
V27__create_pollmessage.sql
V28__superordinate_domain_vkey.sql
V29__add_columns_for_transfer_data.sql
V30__inet_address_converter.sql
V31__client_id_to_registrar_id.sql
V32__drop_unused_transafer_data_columns_in_contact.sql
V33__create_host_history.sql
V34__rename_fully_qualified_names.sql
V35__rename_allow_list.sql
V36__create_safebrowsing_threats.sql
V37__update_spec11threatmatch.sql
V38__create_contact_history.sql
V39__add_updatetime_column.sql
V40__spec11threatmatch_remove_registrar_foreign_key.sql
V41__add_columns_to_domain.sql
V42__add_txn_table.sql
V43__update_relock_duration_type.sql
V44__create_domain_history.sql
V45__add_grace_period_table.sql
V46__Contact_contactId_index_to_non_unique.sql
V47__remove_spec11_domain_foreign_key.sql
V48__domain_add_autorenew_end_time_column.sql
V49__create_allocation_token.sql
V50__use_composite_key_for_registrar_poc.sql
V51__use_composite_primary_key_for_domain_history_table.sql
V52__update_billing_constraint.sql
V53__add_temp_history_id_sequence.sql
V54__add_tld_table.sql
V55__domain_history_fields.sql
V56__rename_host_table.sql
V57__history_null_content.sql
V58__drop_default_value_and_sequences_for_billing_event.sql
V59__use_composite_primary_key_for_contact_and_host_history_table.sql
V60__remove_pollmessage_sequence.sql
V61__domain_hist_columns.sql
V62__disable_key_auto_generation_for_history_tables.sql
V63__add_schema_for_ds_data.sql
@@ -0,0 +1,21 @@
-- Copyright 2020 The Nomulus Authors. All Rights Reserved.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
alter table "BillingCancellation" alter column billing_cancellation_id drop default;
alter table "BillingEvent" alter column billing_event_id drop default;
alter table "BillingRecurrence" alter column billing_recurrence_id drop default;
drop sequence "BillingCancellation_billing_cancellation_id_seq";
drop sequence "BillingEvent_billing_event_id_seq";
drop sequence "BillingRecurrence_billing_recurrence_id_seq";
@@ -0,0 +1,23 @@
-- Copyright 2020 The Nomulus Authors. All Rights Reserved.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
alter table "ContactHistory" drop constraint "ContactHistory_pkey";
alter table "ContactHistory"
add constraint "ContactHistory_pkey" primary key (contact_repo_id, history_revision_id);
alter table "HostHistory" drop constraint "HostHistory_pkey";
alter table "HostHistory"
add constraint "HostHistory_pkey" primary key (host_repo_id, history_revision_id);
@@ -0,0 +1,36 @@
-- Copyright 2020 The Nomulus Authors. All Rights Reserved.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
alter table "PollMessage" alter column poll_message_id drop default;
drop sequence "PollMessage_poll_message_id_seq";
alter table "PollMessage" rename column "domain_revision_id" to "domain_history_revision_id";
alter table "PollMessage" rename column "contact_revision_id" to "contact_history_revision_id";
alter table "PollMessage" rename column "host_revision_id" to "host_history_revision_id";
alter table if exists "PollMessage"
add constraint fk_poll_message_domain_history
foreign key (domain_repo_id, domain_history_revision_id)
references "DomainHistory";
alter table if exists "PollMessage"
add constraint fk_poll_message_contact_history
foreign key (contact_repo_id, contact_history_revision_id)
references "ContactHistory";
alter table if exists "PollMessage"
add constraint fk_poll_message_host_history
foreign key (host_repo_id, host_history_revision_id)
references "HostHistory";
@@ -0,0 +1,39 @@
-- Copyright 2020 The Nomulus Authors. All Rights Reserved.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http:--www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
-- These history ids are technically foreign keys, we don't want to constrain
-- them because they're temporary and we don't need to because they are
-- already indirectly constrained by the relationships with the
-- PollMessages/BillingEvents involved.
ALTER TABLE "Domain" ADD COLUMN billing_recurrence_history_id int8;
ALTER TABLE "Domain" ADD COLUMN autorenew_poll_message_history_id int8;
ALTER TABLE "Domain" ADD COLUMN deletion_poll_message_history_id int8;
ALTER TABLE "DomainHistory" ADD COLUMN billing_recurrence_history_id int8;
ALTER TABLE "DomainHistory" ADD COLUMN autorenew_poll_message_history_id int8;
ALTER TABLE "DomainHistory" ADD COLUMN deletion_poll_message_history_id int8;
-- Drop and re-add DomainHistory's Domain FK constraint to make it deferrable,
-- this breaks a cycle with Domain -> PollMessage|BillingEvent -> History.
ALTER TABLE "DomainHistory" DROP CONSTRAINT fk_domain_history_domain_repo_id;
ALTER TABLE "DomainHistory"
ADD CONSTRAINT fk_domain_history_domain_repo_id
FOREIGN KEY (domain_repo_id) REFERENCES "Domain"(repo_id)
DEFERRABLE INITIALLY DEFERRED;
-- Same for PollMessage -> Domain, breaking Domain -> PollMessage -> Domain.
ALTER TABLE "PollMessage" DROP CONSTRAINT fk_poll_message_domain_repo_id;
ALTER TABLE "PollMessage"
ADD CONSTRAINT fk_poll_message_domain_repo_id
FOREIGN KEY (domain_repo_id) REFERENCES "Domain"(repo_id)
DEFERRABLE INITIALLY DEFERRED;
@@ -0,0 +1,17 @@
-- Copyright 2020 The Nomulus Authors. All Rights Reserved.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
drop sequence "history_id_sequence";
drop sequence "temp_history_id_sequence";
@@ -0,0 +1,29 @@
-- Copyright 2020 The Nomulus Authors. All Rights Reserved.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
create table "DelegationSignerData" (
domain_repo_id text not null,
key_tag int4 not null,
algorithm int4 not null,
digest bytea not null,
digest_type int4 not null,
primary key (domain_repo_id, key_tag)
);
create index IDXhlqqd5uy98cjyos72d81x9j95 on "DelegationSignerData" (domain_repo_id);
alter table if exists "DelegationSignerData"
add constraint FKtr24j9v14ph2mfuw2gsmt12kq
foreign key (domain_repo_id)
references "Domain";
@@ -11,7 +11,6 @@
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
create sequence temp_history_id_sequence start 1 increment 50;
create table "AllocationToken" (
token text not null,
@@ -30,7 +29,7 @@ create sequence temp_history_id_sequence start 1 increment 50;
);
create table "BillingCancellation" (
billing_cancellation_id bigserial not null,
billing_cancellation_id int8 not null,
registrar_id text not null,
domain_history_revision_id int8 not null,
domain_repo_id text not null,
@@ -45,7 +44,7 @@ create sequence temp_history_id_sequence start 1 increment 50;
);
create table "BillingEvent" (
billing_event_id bigserial not null,
billing_event_id int8 not null,
registrar_id text not null,
domain_history_revision_id int8 not null,
domain_repo_id text not null,
@@ -64,7 +63,7 @@ create sequence temp_history_id_sequence start 1 increment 50;
);
create table "BillingRecurrence" (
billing_recurrence_id bigserial not null,
billing_recurrence_id int8 not null,
registrar_id text not null,
domain_history_revision_id int8 not null,
domain_repo_id text not null,
@@ -151,7 +150,8 @@ create sequence temp_history_id_sequence start 1 increment 50;
);
create table "ContactHistory" (
history_revision_id int8 not null,
contact_repo_id text not null,
history_revision_id int8 not null,
history_by_superuser boolean not null,
history_registrar_id text,
history_modification_time timestamptz not null,
@@ -215,8 +215,7 @@ create sequence temp_history_id_sequence start 1 increment 50;
last_epp_update_time timestamptz,
statuses text[],
update_timestamp timestamptz,
contact_repo_id text not null,
primary key (history_revision_id)
primary key (contact_repo_id, history_revision_id)
);
create table "Cursor" (
@@ -228,11 +227,12 @@ create sequence temp_history_id_sequence start 1 increment 50;
);
create table "DelegationSignerData" (
key_tag int4 not null,
domain_repo_id text not null,
key_tag int4 not null,
algorithm int4 not null,
digest bytea,
digest bytea not null,
digest_type int4 not null,
primary key (key_tag)
primary key (domain_repo_id, key_tag)
);
create table "Domain" (
@@ -249,10 +249,13 @@ create sequence temp_history_id_sequence start 1 increment 50;
auth_info_repo_id text,
auth_info_value text,
billing_recurrence_id int8,
billing_recurrence_history_id int8,
autorenew_end_time timestamptz,
autorenew_poll_message_id int8,
autorenew_poll_message_history_id int8,
billing_contact text,
deletion_poll_message_id int8,
deletion_poll_message_history_id int8,
domain_name text,
idn_table_name text,
last_transfer_time timestamptz,
@@ -301,10 +304,13 @@ create sequence temp_history_id_sequence start 1 increment 50;
auth_info_repo_id text,
auth_info_value text,
billing_recurrence_id int8,
billing_recurrence_history_id int8,
autorenew_end_time timestamptz,
autorenew_poll_message_id int8,
autorenew_poll_message_history_id int8,
billing_contact text,
deletion_poll_message_id int8,
deletion_poll_message_history_id int8,
domain_name text,
idn_table_name text,
last_transfer_time timestamptz,
@@ -400,7 +406,8 @@ create sequence temp_history_id_sequence start 1 increment 50;
);
create table "HostHistory" (
history_revision_id int8 not null,
host_repo_id text not null,
history_revision_id int8 not null,
history_by_superuser boolean not null,
history_registrar_id text,
history_modification_time timestamptz not null,
@@ -423,8 +430,7 @@ create sequence temp_history_id_sequence start 1 increment 50;
last_epp_update_time timestamptz,
statuses text[],
update_timestamp timestamptz,
host_repo_id text not null,
primary key (history_revision_id)
primary key (host_repo_id, history_revision_id)
);
create table "Lock" (
@@ -438,15 +444,15 @@ create sequence temp_history_id_sequence start 1 increment 50;
create table "PollMessage" (
type text not null,
poll_message_id bigserial not null,
poll_message_id int8 not null,
registrar_id text not null,
contact_history_revision_id int8,
contact_repo_id text,
contact_revision_id int8,
domain_history_revision_id int8,
domain_repo_id text,
domain_revision_id int8,
event_time timestamptz not null,
host_history_revision_id int8,
host_repo_id text,
host_revision_id int8,
message text,
transfer_response_contact_id text,
transfer_response_domain_expiration_time timestamptz,
@@ -663,6 +669,7 @@ create index IDXo1xdtpij2yryh0skxe9v91sep on "ContactHistory" (creation_time);
create index IDXhp33wybmb6tbpr1bq7ttwk8je on "ContactHistory" (history_registrar_id);
create index IDX9q53px6r302ftgisqifmc6put on "ContactHistory" (history_type);
create index IDXsudwswtwqnfnx2o1hx4s0k0g5 on "ContactHistory" (history_modification_time);
create index IDXhlqqd5uy98cjyos72d81x9j95 on "DelegationSignerData" (domain_repo_id);
create index IDX8nr0ke9mrrx4ewj6pd2ag4rmr on "Domain" (creation_time);
create index IDXhsjqiy2lyobfymplb28nm74lm on "Domain" (current_sponsor_registrar_id);
create index IDX5mnf0wn20tno4b9do88j61klr on "Domain" (deletion_time);
@@ -700,6 +707,11 @@ create index spec11threatmatch_check_date_idx on "Spec11ThreatMatch" (check_date
foreign key (revision_id)
references "ClaimsList";
alter table if exists "DelegationSignerData"
add constraint FKtr24j9v14ph2mfuw2gsmt12kq
foreign key (domain_repo_id)
references "Domain";
alter table if exists "DomainHistoryHost"
add constraint FKa9woh3hu8gx5x0vly6bai327n
foreign key (domain_history_domain_repo_id, domain_history_history_revision_id)
@@ -73,25 +73,6 @@ CREATE TABLE public."BillingCancellation" (
);
--
-- Name: BillingCancellation_billing_cancellation_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public."BillingCancellation_billing_cancellation_id_seq"
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: BillingCancellation_billing_cancellation_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public."BillingCancellation_billing_cancellation_id_seq" OWNED BY public."BillingCancellation".billing_cancellation_id;
--
-- Name: BillingEvent; Type: TABLE; Schema: public; Owner: -
--
@@ -115,25 +96,6 @@ CREATE TABLE public."BillingEvent" (
);
--
-- Name: BillingEvent_billing_event_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public."BillingEvent_billing_event_id_seq"
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: BillingEvent_billing_event_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public."BillingEvent_billing_event_id_seq" OWNED BY public."BillingEvent".billing_event_id;
--
-- Name: BillingRecurrence; Type: TABLE; Schema: public; Owner: -
--
@@ -152,25 +114,6 @@ CREATE TABLE public."BillingRecurrence" (
);
--
-- Name: BillingRecurrence_billing_recurrence_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public."BillingRecurrence_billing_recurrence_id_seq"
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: BillingRecurrence_billing_recurrence_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public."BillingRecurrence_billing_recurrence_id_seq" OWNED BY public."BillingRecurrence".billing_recurrence_id;
--
-- Name: ClaimsEntry; Type: TABLE; Schema: public; Owner: -
--
@@ -360,6 +303,19 @@ CREATE TABLE public."Cursor" (
);
--
-- Name: DelegationSignerData; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public."DelegationSignerData" (
domain_repo_id text NOT NULL,
key_tag integer NOT NULL,
algorithm integer NOT NULL,
digest bytea NOT NULL,
digest_type integer NOT NULL
);
--
-- Name: Domain; Type: TABLE; Schema: public; Owner: -
--
@@ -410,7 +366,10 @@ CREATE TABLE public."Domain" (
billing_recurrence_id bigint,
autorenew_poll_message_id bigint,
deletion_poll_message_id bigint,
autorenew_end_time timestamp with time zone
autorenew_end_time timestamp with time zone,
billing_recurrence_history_id bigint,
autorenew_poll_message_history_id bigint,
deletion_poll_message_history_id bigint
);
@@ -477,7 +436,10 @@ CREATE TABLE public."DomainHistory" (
autorenew_end_time timestamp with time zone,
history_other_registrar_id text,
history_period_unit text,
history_period_value integer
history_period_value integer,
billing_recurrence_history_id bigint,
autorenew_poll_message_history_id bigint,
deletion_poll_message_history_id bigint
);
@@ -646,12 +608,12 @@ CREATE TABLE public."PollMessage" (
poll_message_id bigint NOT NULL,
registrar_id text NOT NULL,
contact_repo_id text,
contact_revision_id bigint,
contact_history_revision_id bigint,
domain_repo_id text,
domain_revision_id bigint,
domain_history_revision_id bigint,
event_time timestamp with time zone NOT NULL,
host_repo_id text,
host_revision_id bigint,
host_history_revision_id bigint,
message text,
transfer_response_contact_id text,
transfer_response_domain_expiration_time timestamp with time zone,
@@ -671,25 +633,6 @@ CREATE TABLE public."PollMessage" (
);
--
-- Name: PollMessage_poll_message_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public."PollMessage_poll_message_id_seq"
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: PollMessage_poll_message_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public."PollMessage_poll_message_id_seq" OWNED BY public."PollMessage".poll_message_id;
--
-- Name: PremiumEntry; Type: TABLE; Schema: public; Owner: -
--
@@ -1001,51 +944,6 @@ CREATE SEQUENCE public."Transaction_id_seq"
ALTER SEQUENCE public."Transaction_id_seq" OWNED BY public."Transaction".id;
--
-- Name: history_id_sequence; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public.history_id_sequence
START WITH 1
INCREMENT BY 50
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: temp_history_id_sequence; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public.temp_history_id_sequence
START WITH 1
INCREMENT BY 50
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: BillingCancellation billing_cancellation_id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."BillingCancellation" ALTER COLUMN billing_cancellation_id SET DEFAULT nextval('public."BillingCancellation_billing_cancellation_id_seq"'::regclass);
--
-- Name: BillingEvent billing_event_id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."BillingEvent" ALTER COLUMN billing_event_id SET DEFAULT nextval('public."BillingEvent_billing_event_id_seq"'::regclass);
--
-- Name: BillingRecurrence billing_recurrence_id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."BillingRecurrence" ALTER COLUMN billing_recurrence_id SET DEFAULT nextval('public."BillingRecurrence_billing_recurrence_id_seq"'::regclass);
--
-- Name: ClaimsList revision_id; Type: DEFAULT; Schema: public; Owner: -
--
@@ -1067,13 +965,6 @@ ALTER TABLE ONLY public."DomainTransactionRecord" ALTER COLUMN id SET DEFAULT ne
ALTER TABLE ONLY public."GracePeriod" ALTER COLUMN id SET DEFAULT nextval('public."GracePeriod_id_seq"'::regclass);
--
-- Name: PollMessage poll_message_id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."PollMessage" ALTER COLUMN poll_message_id SET DEFAULT nextval('public."PollMessage_poll_message_id_seq"'::regclass);
--
-- Name: PremiumList revision_id; Type: DEFAULT; Schema: public; Owner: -
--
@@ -1162,7 +1053,7 @@ ALTER TABLE ONLY public."ClaimsList"
--
ALTER TABLE ONLY public."ContactHistory"
ADD CONSTRAINT "ContactHistory_pkey" PRIMARY KEY (history_revision_id);
ADD CONSTRAINT "ContactHistory_pkey" PRIMARY KEY (contact_repo_id, history_revision_id);
--
@@ -1181,6 +1072,14 @@ ALTER TABLE ONLY public."Cursor"
ADD CONSTRAINT "Cursor_pkey" PRIMARY KEY (scope, type);
--
-- Name: DelegationSignerData DelegationSignerData_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."DelegationSignerData"
ADD CONSTRAINT "DelegationSignerData_pkey" PRIMARY KEY (domain_repo_id, key_tag);
--
-- Name: DomainHistory DomainHistory_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -1218,7 +1117,7 @@ ALTER TABLE ONLY public."GracePeriod"
--
ALTER TABLE ONLY public."HostHistory"
ADD CONSTRAINT "HostHistory_pkey" PRIMARY KEY (history_revision_id);
ADD CONSTRAINT "HostHistory_pkey" PRIMARY KEY (host_repo_id, history_revision_id);
--
@@ -1494,6 +1393,13 @@ CREATE INDEX idxeokttmxtpq2hohcioe5t2242b ON public."BillingCancellation" USING
CREATE INDEX idxfg2nnjlujxo6cb9fha971bq2n ON public."HostHistory" USING btree (creation_time);
--
-- Name: idxhlqqd5uy98cjyos72d81x9j95; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX idxhlqqd5uy98cjyos72d81x9j95 ON public."DelegationSignerData" USING btree (domain_repo_id);
--
-- Name: idxhmv411mdqo5ibn4vy7ykxpmlv; Type: INDEX; Schema: public; Owner: -
--
@@ -1872,7 +1778,7 @@ ALTER TABLE ONLY public."Domain"
--
ALTER TABLE ONLY public."DomainHistory"
ADD CONSTRAINT fk_domain_history_domain_repo_id FOREIGN KEY (domain_repo_id) REFERENCES public."Domain"(repo_id);
ADD CONSTRAINT fk_domain_history_domain_repo_id FOREIGN KEY (domain_repo_id) REFERENCES public."Domain"(repo_id) DEFERRABLE INITIALLY DEFERRED;
--
@@ -1979,6 +1885,14 @@ ALTER TABLE ONLY public."HostHistory"
ADD CONSTRAINT fk_hosthistory_host FOREIGN KEY (host_repo_id) REFERENCES public."Host"(repo_id);
--
-- Name: PollMessage fk_poll_message_contact_history; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."PollMessage"
ADD CONSTRAINT fk_poll_message_contact_history FOREIGN KEY (contact_repo_id, contact_history_revision_id) REFERENCES public."ContactHistory"(contact_repo_id, history_revision_id);
--
-- Name: PollMessage fk_poll_message_contact_repo_id; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@@ -1987,12 +1901,28 @@ ALTER TABLE ONLY public."PollMessage"
ADD CONSTRAINT fk_poll_message_contact_repo_id FOREIGN KEY (contact_repo_id) REFERENCES public."Contact"(repo_id);
--
-- Name: PollMessage fk_poll_message_domain_history; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."PollMessage"
ADD CONSTRAINT fk_poll_message_domain_history FOREIGN KEY (domain_repo_id, domain_history_revision_id) REFERENCES public."DomainHistory"(domain_repo_id, history_revision_id);
--
-- Name: PollMessage fk_poll_message_domain_repo_id; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."PollMessage"
ADD CONSTRAINT fk_poll_message_domain_repo_id FOREIGN KEY (domain_repo_id) REFERENCES public."Domain"(repo_id);
ADD CONSTRAINT fk_poll_message_domain_repo_id FOREIGN KEY (domain_repo_id) REFERENCES public."Domain"(repo_id) DEFERRABLE INITIALLY DEFERRED;
--
-- Name: PollMessage fk_poll_message_host_history; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."PollMessage"
ADD CONSTRAINT fk_poll_message_host_history FOREIGN KEY (host_repo_id, host_history_revision_id) REFERENCES public."HostHistory"(host_repo_id, history_revision_id);
--
@@ -2083,6 +2013,14 @@ ALTER TABLE ONLY public."PremiumEntry"
ADD CONSTRAINT fko0gw90lpo1tuee56l0nb6y6g5 FOREIGN KEY (revision_id) REFERENCES public."PremiumList"(revision_id);
--
-- Name: DelegationSignerData fktr24j9v14ph2mfuw2gsmt12kq; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."DelegationSignerData"
ADD CONSTRAINT fktr24j9v14ph2mfuw2gsmt12kq FOREIGN KEY (domain_repo_id) REFERENCES public."Domain"(repo_id);
--
-- PostgreSQL database dump complete
--
@@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.truth.TextDiffSubject.assertThat;
import com.google.common.base.Joiner;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.Resources;
import google.registry.persistence.NomulusPostgreSql;
import java.io.File;
@@ -26,16 +27,41 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import org.flywaydb.core.Flyway;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.Container.ExecResult;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
/** Unit tests about Cloud SQL schema. */
/**
* Schema deployment tests using Flyway.
*
* <p>This class has two test methods:
*
* <ul>
* <li>{@link #deploySchema_emptyDb()} is invoked only in UNIT tests (:db:test in Gradle). It
* deploys the entire set of Flyway scripts (found on classpath) to an empty database and
* compares the resulting schema with the golden schema.
* <li>{@link #deploySchema_existingDb()} is invoked only in an integration test
* (:db:schemaIncrementalDeployTest in Gradle). It first populates the test database with an
* earlier release of the schema (found on the classpath), then deploys the latest Flyway
* scripts (found on local filesystem under the resources directory) to that database. This
* test detects all forbidden changes to deployed scripts including content change, file
* renaming, and file deletion.
* <p>This test also checks for out-of-order version numbers, i.e., new scripts with lower
* numbers than that of the last deployed script. Out-of-order versions are confusing to
* maintainers, however, Flyway does not provide ways to check before schema deployment. In
* this test, out-of-order scripts are ignored in the incremental-deployment phase (default
* Flyway behavior). The final validate call will fail on them.
* </ul>
*/
@Testcontainers
class SchemaTest {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
// Resource path that is mapped to the testcontainer instance.
private static final String MOUNTED_RESOURCE_PATH = "testcontainer/mount";
// The mount point in the container.
@@ -57,7 +83,8 @@ class SchemaTest {
MOUNTED_RESOURCE_PATH, CONTAINER_MOUNT_POINT, BindMode.READ_WRITE);
@Test
void deploySchema_success() throws Exception {
@DisabledIfSystemProperty(named = "deploy_to_existing_db", matches = ".*")
void deploySchema_emptyDb() throws Exception {
Flyway flyway =
Flyway.configure()
.locations("sql/flyway")
@@ -86,6 +113,32 @@ class SchemaTest {
.hasSameContentAs(Resources.getResource("sql/schema/nomulus.golden.sql"));
}
@Test
@EnabledIfSystemProperty(named = "deploy_to_existing_db", matches = ".*")
void deploySchema_existingDb() {
// Initialize database with the base schema, which is on the classpath.
Flyway flyway =
Flyway.configure()
.locations("sql/flyway")
.dataSource(
sqlContainer.getJdbcUrl(), sqlContainer.getUsername(), sqlContainer.getPassword())
.load();
flyway.migrate();
logger.atInfo().log("Base schema version: %s", flyway.info().current().getVersion().toString());
// Deploy latest scripts from resources directory.
flyway =
Flyway.configure()
.locations("filesystem:build/resources/main/sql/flyway")
.dataSource(
sqlContainer.getJdbcUrl(), sqlContainer.getUsername(), sqlContainer.getPassword())
.load();
flyway.migrate();
flyway.validate();
logger.atInfo().log(
"Latest schema version: %s", flyway.info().current().getVersion().toString());
}
private static String[] getSchemaDumpCommand(String username, String dbName) {
return new String[] {
"pg_dump",
+3
View File
@@ -124,6 +124,7 @@ ext {
'org.apache.ftpserver:ftpserver-core:1.0.6',
'org.apache.httpcomponents:httpclient:4.5.11',
'org.apache.httpcomponents:httpcore:4.4.13',
'org.apache.logging.log4j:log4j-core:2.13.3',
'org.apache.sshd:sshd-core:2.0.0',
'org.apache.sshd:sshd-scp:2.0.0',
'org.apache.sshd:sshd-sftp:2.0.0',
@@ -156,6 +157,8 @@ ext {
'org.testcontainers:selenium:1.14.3',
'org.testcontainers:testcontainers:1.14.3',
'org.yaml:snakeyaml:1.17',
'us.fatehi:schemacrawler:16.10.1',
'us.fatehi:schemacrawler-diagram:16.10.1',
'xerces:xmlParserAPIs:2.6.2',
'xpp3:xpp3:1.1.4c'
]
@@ -201,8 +201,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -251,5 +253,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -199,8 +199,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -247,5 +249,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -201,8 +201,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -251,5 +253,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -200,8 +200,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +251,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -201,8 +201,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -251,5 +253,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -200,8 +200,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +251,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -204,9 +204,11 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.apiguardian:apiguardian-api:1.1.0
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -261,5 +263,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -203,9 +203,11 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.apiguardian:apiguardian-api:1.1.0
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -259,5 +261,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -204,9 +204,11 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.apiguardian:apiguardian-api:1.1.0
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -261,5 +263,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -204,9 +204,11 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.apiguardian:apiguardian-api:1.1.0
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -261,5 +263,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
+1
View File
@@ -11,6 +11,7 @@ You will need the following programs installed on your local machine:
* [Google App Engine SDK for Java][app-engine-sdk], and configure aliases to
to the `gcloud` and `appcfg.sh` utilities (you'll use them a lot).
* [Git](https://git-scm.com/) version control system.
* Python version 3.7 or newer.
**Note:** App Engine does not yet support Java 9. Also, the instructions in this
document have only been tested on Linux. They might work with some alterations
+1
View File
@@ -24,6 +24,7 @@ dbName=postgres
dbUser=
dbPassword=
publish_repo=
baseSchemaTag=
schema_version=
nomulus_version=
dot_path=/usr/bin/dot
+2
View File
@@ -79,6 +79,8 @@ function runTest() {
echo "Running test with -Pnomulus_version=${nomulus_version}" \
"-Pschema_version=${schema_version}"
# The https scheme in the Maven repo URL below is required for Kokoro. See
# ./run_schema_check.sh for more information.
(cd ${SCRIPT_DIR}/..; \
./gradlew :integration:sqlIntegrationTest \
-PdevProject=${dev_project} \
+17 -18
View File
@@ -25,12 +25,13 @@ $(basename "$0") OPTIONS
Checks for post-deployment change to Flyway scripts.
With Flyway, once an incremental change script is deployed, it must not be
changed. Even changes to comments or whitespaces would cause validation
failures during future deployment. This script checks for changes (including
removal and renaming which may happen due to incorrect merge conflict
resolution) to scripts that have already been deployed to Sandbox. The
assumption is that the schema in Sandbox is always newer than that in
production.
edited, renamed, or deleted. This script checks for changes to scripts that have
already been deployed to Sandbox. The assumption is that the schema in Sandbox
is always at least as recent as that in production. Please refer to Gradle task
:db:schemaIncrementalDeployTest for more information.
Note that this test MAY fail to catch forbidden changes during the period when
a new schema release is created but not yet deployed to Sandbox.
A side-effect of this check is that old branches missing recently deployed
scripts must update first.
@@ -65,16 +66,14 @@ fi
sandbox_tag=$(fetchVersion sql sandbox ${DEV_PROJECT})
echo "Checking Flyway scripts against schema in Sandbox (${sandbox_tag})."
modified_sqls=$(git diff --name-status ${sandbox_tag} \
db/src/main/resources/sql/flyway | grep "^M\|^D\|^R" | grep \.sql$ | wc -l)
if [[ ${modified_sqls} = 0 ]]; then
echo "No illegal change to deployed schema scripts."
exit 0
else
echo "Changes to the following files are not allowed:"
echo $(git diff --name-status ${sandbox_tag} \
db/src/main/resources/sql/flyway | grep "^M\|^D\|^R" | grep \.sql$)
echo "Make sure your branch is up to date with HEAD of master."
exit 1
fi
# The URL of the Maven repo on GCS for the publish_repo parameter must use the
# https scheme (https://storage.googleapis.com/{BUCKET}/{PATH}) in order to work
# with Kokoro. Gradle's alternative gcs scheme does not work on Kokoro: a GCP
# credential with proper scopes for GCS access is required even for public
# buckets, however, Kokoro VM instances are not set up with such credentials.
# Incidentally, gcs can be used on Cloud Build.
(cd ${SCRIPT_DIR}/..; \
./gradlew :db:schemaIncrementalDeployTest \
-PbaseSchemaTag=${sandbox_tag} \
-Ppublish_repo=https://storage.googleapis.com/${DEV_PROJECT}-deployed-tags/maven)
@@ -25,7 +25,7 @@ import com.google.common.collect.ImmutableList;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
import google.registry.networking.util.SelfSignedCaCertificate;
import google.registry.util.SelfSignedCaCertificate;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -21,7 +21,7 @@ import static google.registry.networking.handler.SslInitializerTestUtils.signKey
import static google.registry.networking.handler.SslInitializerTestUtils.verifySslException;
import com.google.common.collect.ImmutableList;
import google.registry.networking.util.SelfSignedCaCertificate;
import google.registry.util.SelfSignedCaCertificate;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelPipeline;
@@ -18,7 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.base.Throwables;
import google.registry.networking.util.SelfSignedCaCertificate;
import google.registry.util.SelfSignedCaCertificate;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.handler.ssl.SslHandler;
@@ -23,7 +23,7 @@ import static google.registry.networking.handler.SslServerInitializer.CLIENT_CER
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import google.registry.networking.util.SelfSignedCaCertificate;
import google.registry.util.SelfSignedCaCertificate;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
@@ -26,7 +26,7 @@ import dagger.Component;
import dagger.Module;
import dagger.Provides;
import google.registry.networking.module.CertificateSupplierModule.Mode;
import google.registry.networking.util.SelfSignedCaCertificate;
import google.registry.util.SelfSignedCaCertificate;
import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
import java.security.KeyPair;
@@ -18,9 +18,6 @@ spec:
containers:
- name: proxy-canary
image: gcr.io/GCP_PROJECT/proxy
resources:
requests:
cpu: "101m"
ports:
- containerPort: 30000
name: health-check
@@ -18,9 +18,6 @@ spec:
containers:
- name: proxy
image: gcr.io/GCP_PROJECT/proxy
resources:
requests:
cpu: "101m"
ports:
- containerPort: 30000
name: health-check
@@ -23,9 +23,9 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.base.Throwables;
import google.registry.networking.util.SelfSignedCaCertificate;
import google.registry.proxy.handler.HttpsRelayServiceHandler.NonOkHttpResponseException;
import google.registry.testing.FakeClock;
import google.registry.util.SelfSignedCaCertificate;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
@@ -27,10 +27,10 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import com.google.common.base.Throwables;
import google.registry.networking.util.SelfSignedCaCertificate;
import google.registry.proxy.TestUtils;
import google.registry.proxy.handler.HttpsRelayServiceHandler.NonOkHttpResponseException;
import google.registry.proxy.metric.FrontendMetrics;
import google.registry.util.SelfSignedCaCertificate;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInitializer;
+33 -11
View File
@@ -63,17 +63,6 @@ steps:
- --credential
- tool-credential.json
- deploy_invoicing_pipeline
# Save the deployed tag for the current environment on GCS. Because of b/137891685
# which causes the for-loop in the next step to fail, this may not be the last step.
# TODO(weiminyu): do this in last step.
- name: 'gcr.io/$PROJECT_ID/builder:latest'
entrypoint: /bin/bash
args:
- -c
- |
set -e
echo ${TAG_NAME} | \
gsutil cp - gs://$PROJECT_ID-deployed-tags/nomulus.${_ENV}.tag
# Deploy the GAE config files.
# First authorize the gcloud tool to use the credential json file, then
# download and unzip the tarball that contains the relevant config files
@@ -97,6 +86,39 @@ steps:
gcloud -q --project $project_id app deploy \
default/WEB-INF/appengine-generated/$filename.yaml
done
# Save the deployed tag for the current environment on GCS, and update the
# mappings from Nomulus releases to Appengine versions.
- name: 'gcr.io/$PROJECT_ID/builder:latest'
entrypoint: /bin/bash
args:
- -c
- |
set -e
echo ${TAG_NAME} | \
gsutil cp - gs://$PROJECT_ID-deployed-tags/nomulus.${_ENV}.tag
# Update the release to AppEngine version mapping.
if [ ${_ENV} == production ]; then
project_id="domain-registry"
else
project_id="domain-registry-${_ENV}"
fi
local_map="nomulus.${_ENV}.tmp"
gcloud app versions list \
--project $project_id --hide-no-traffic \
--format="csv[no-heading](SERVICE,VERSION.ID)" | \
grep -e "^backend\|^default\|^pubapi\|^tools" |\
while read line; do echo "${TAG_NAME},$line"; done | tee "$local_map"
num_versions=$(cat "$local_map" | wc -l)
if [ "$num_versions" -ne 4 ]; then
echo "Expecting exactly four active services. Found $num_versions"
exit 1
fi
gsutil cp "$local_map" gs://$PROJECT_ID-deployed-tags/nomulus.${_ENV}.tmp
# Atomically append uploaded tmp file to nomulus.${_ENV}.versions
gsutil compose \
gs://$PROJECT_ID-deployed-tags/nomulus.${_ENV}.versions \
gs://$PROJECT_ID-deployed-tags/nomulus.${_ENV}.tmp \
gs://$PROJECT_ID-deployed-tags/nomulus.${_ENV}.versions
timeout: 3600s
options:
@@ -200,8 +200,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +251,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -200,8 +200,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +251,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -200,8 +200,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +251,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -200,8 +200,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +251,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -200,8 +200,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +251,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -200,8 +200,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +251,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -200,8 +200,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +251,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -200,8 +200,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +251,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c
@@ -200,8 +200,10 @@ org.apache.commons:commons-compress:1.20
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apache.logging.log4j:log4j-api:2.6.2
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-core:2.13.3
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
@@ -249,5 +251,10 @@ org.tukaani:xz:1.8
org.w3c.css:sac:1.3
org.xerial.snappy:snappy-java:1.1.4
org.yaml:snakeyaml:1.17
us.fatehi:schemacrawler-api:16.10.1
us.fatehi:schemacrawler-diagram:16.10.1
us.fatehi:schemacrawler-tools:16.10.1
us.fatehi:schemacrawler-utility:16.10.1
us.fatehi:schemacrawler:16.10.1
xerces:xmlParserAPIs:2.6.2
xpp3:xpp3:1.1.4c

Some files were not shown because too many files have changed in this diff Show More