mirror of
https://github.com/google/nomulus
synced 2026-06-09 16:33:02 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1f4cf5bdb6 | |||
| 4176f7dd9c | |||
| e07139665e | |||
| b75eb0ad95 | |||
| 63f8fcef18 | |||
| 8d563c4516 | |||
| a58c4a6492 | |||
| e59475a5f6 | |||
| c35f92f54b | |||
| cd415fe846 | |||
| 4ca2c11b20 | |||
| 4e44a98139 | |||
| d4371a880e | |||
| 7312bc9e60 | |||
| 83ca9d82df | |||
| 8724ef6c70 |
+2
-2
@@ -121,7 +121,7 @@ task stage {
|
||||
// App-engine environment configuration. We set up all of the variables in
|
||||
// the root project.
|
||||
|
||||
def environments = ['production', 'sandbox', 'alpha', 'crash']
|
||||
def environments = ['production', 'sandbox', 'alpha', 'crash', 'qa']
|
||||
|
||||
def gcpProject = null
|
||||
|
||||
@@ -155,7 +155,7 @@ def verifyDeploymentParams() {
|
||||
System.err.println('-----------------------------------------------------------------')
|
||||
throw new GradleException('Aborting. See prominent error above.')
|
||||
} else if (gcpProject == null) {
|
||||
def error = 'You must specify -P environment={alpha,crash}'
|
||||
def error = 'You must specify -P environment={alpha,crash,qa}'
|
||||
System.err.println("\033[33;1m${error}\033[0m")
|
||||
throw GradleException("Aborting: ${error}")
|
||||
}
|
||||
|
||||
@@ -71,12 +71,13 @@ dependencies {
|
||||
def deps = dependencyMap
|
||||
compile deps['com.google.auth:google-auth-library-credentials']
|
||||
compile deps['com.google.auth:google-auth-library-oauth2-http']
|
||||
compile deps['com.google.cloud:google-cloud-core']
|
||||
compile deps['com.google.guava:guava']
|
||||
compile deps['com.google.auto.value:auto-value-annotations']
|
||||
compile deps['com.google.cloud:google-cloud-core']
|
||||
compile deps['com.google.cloud:google-cloud-storage']
|
||||
compile deps['org.apache.commons:commons-text']
|
||||
compile deps['com.google.guava:guava']
|
||||
compile deps['com.google.protobuf:protobuf-java']
|
||||
compile deps['com.google.template:soy']
|
||||
compile deps['org.apache.commons:commons-text']
|
||||
annotationProcessor deps['com.google.auto.value:auto-value']
|
||||
testCompile deps['com.google.truth:truth']
|
||||
testCompile deps['com.google.truth.extensions:truth-java8-extension']
|
||||
|
||||
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
com.google.oauth-client:google-oauth-client:1.27.0
|
||||
com.google.protobuf:protobuf-java-util:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.13.0
|
||||
com.google.template:soy:2018-03-14
|
||||
com.ibm.icu:icu4j:57.1
|
||||
commons-codec:commons-codec:1.11
|
||||
|
||||
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
com.google.oauth-client:google-oauth-client:1.27.0
|
||||
com.google.protobuf:protobuf-java-util:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.13.0
|
||||
com.google.template:soy:2018-03-14
|
||||
com.ibm.icu:icu4j:57.1
|
||||
commons-codec:commons-codec:1.11
|
||||
|
||||
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
com.google.oauth-client:google-oauth-client:1.27.0
|
||||
com.google.protobuf:protobuf-java-util:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.13.0
|
||||
com.google.template:soy:2018-03-14
|
||||
com.ibm.icu:icu4j:57.1
|
||||
commons-codec:commons-codec:1.11
|
||||
|
||||
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
com.google.oauth-client:google-oauth-client:1.27.0
|
||||
com.google.protobuf:protobuf-java-util:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.13.0
|
||||
com.google.template:soy:2018-03-14
|
||||
com.ibm.icu:icu4j:57.1
|
||||
commons-codec:commons-codec:1.11
|
||||
|
||||
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
com.google.oauth-client:google-oauth-client:1.27.0
|
||||
com.google.protobuf:protobuf-java-util:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.13.0
|
||||
com.google.template:soy:2018-03-14
|
||||
com.ibm.icu:icu4j:57.1
|
||||
commons-codec:commons-codec:1.11
|
||||
|
||||
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
com.google.oauth-client:google-oauth-client:1.27.0
|
||||
com.google.protobuf:protobuf-java-util:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.13.0
|
||||
com.google.template:soy:2018-03-14
|
||||
com.google.truth.extensions:truth-java8-extension:1.0
|
||||
com.google.truth:truth:1.0
|
||||
@@ -50,8 +50,8 @@ javax.inject:javax.inject:1
|
||||
javax.validation:validation-api:1.0.0.GA
|
||||
joda-time:joda-time:2.9.2
|
||||
junit:junit:4.12
|
||||
net.bytebuddy:byte-buddy-agent:1.10.5
|
||||
net.bytebuddy:byte-buddy:1.10.5
|
||||
net.bytebuddy:byte-buddy-agent:1.10.19
|
||||
net.bytebuddy:byte-buddy:1.10.19
|
||||
org.apache.commons:commons-lang3:3.8.1
|
||||
org.apache.commons:commons-text:1.6
|
||||
org.apache.httpcomponents:httpclient:4.5.8
|
||||
@@ -66,8 +66,8 @@ org.junit.jupiter:junit-jupiter-engine:5.6.2
|
||||
org.junit.platform:junit-platform-commons:1.6.2
|
||||
org.junit.platform:junit-platform-engine:1.6.2
|
||||
org.junit:junit-bom:5.6.2
|
||||
org.mockito:mockito-core:3.3.3
|
||||
org.objenesis:objenesis:2.6
|
||||
org.mockito:mockito-core:3.7.7
|
||||
org.objenesis:objenesis:3.1
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
org.ow2.asm:asm-analysis:6.0
|
||||
org.ow2.asm:asm-commons:6.0
|
||||
|
||||
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
com.google.oauth-client:google-oauth-client:1.27.0
|
||||
com.google.protobuf:protobuf-java-util:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.13.0
|
||||
com.google.template:soy:2018-03-14
|
||||
com.google.truth.extensions:truth-java8-extension:1.0
|
||||
com.google.truth:truth:1.0
|
||||
@@ -50,8 +50,8 @@ javax.inject:javax.inject:1
|
||||
javax.validation:validation-api:1.0.0.GA
|
||||
joda-time:joda-time:2.9.2
|
||||
junit:junit:4.12
|
||||
net.bytebuddy:byte-buddy-agent:1.10.5
|
||||
net.bytebuddy:byte-buddy:1.10.5
|
||||
net.bytebuddy:byte-buddy-agent:1.10.19
|
||||
net.bytebuddy:byte-buddy:1.10.19
|
||||
org.apache.commons:commons-lang3:3.8.1
|
||||
org.apache.commons:commons-text:1.6
|
||||
org.apache.httpcomponents:httpclient:4.5.8
|
||||
@@ -66,8 +66,8 @@ org.junit.jupiter:junit-jupiter-engine:5.6.2
|
||||
org.junit.platform:junit-platform-commons:1.6.2
|
||||
org.junit.platform:junit-platform-engine:1.6.2
|
||||
org.junit:junit-bom:5.6.2
|
||||
org.mockito:mockito-core:3.3.3
|
||||
org.objenesis:objenesis:2.6
|
||||
org.mockito:mockito-core:3.7.7
|
||||
org.objenesis:objenesis:3.1
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
org.ow2.asm:asm-analysis:6.0
|
||||
org.ow2.asm:asm-commons:6.0
|
||||
|
||||
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
com.google.oauth-client:google-oauth-client:1.27.0
|
||||
com.google.protobuf:protobuf-java-util:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.13.0
|
||||
com.google.template:soy:2018-03-14
|
||||
com.google.truth.extensions:truth-java8-extension:1.0
|
||||
com.google.truth:truth:1.0
|
||||
@@ -50,8 +50,8 @@ javax.inject:javax.inject:1
|
||||
javax.validation:validation-api:1.0.0.GA
|
||||
joda-time:joda-time:2.9.2
|
||||
junit:junit:4.12
|
||||
net.bytebuddy:byte-buddy-agent:1.10.5
|
||||
net.bytebuddy:byte-buddy:1.10.5
|
||||
net.bytebuddy:byte-buddy-agent:1.10.19
|
||||
net.bytebuddy:byte-buddy:1.10.19
|
||||
org.apache.commons:commons-lang3:3.8.1
|
||||
org.apache.commons:commons-text:1.6
|
||||
org.apache.httpcomponents:httpclient:4.5.8
|
||||
@@ -66,8 +66,8 @@ org.junit.jupiter:junit-jupiter-engine:5.6.2
|
||||
org.junit.platform:junit-platform-commons:1.6.2
|
||||
org.junit.platform:junit-platform-engine:1.6.2
|
||||
org.junit:junit-bom:5.6.2
|
||||
org.mockito:mockito-core:3.3.3
|
||||
org.objenesis:objenesis:2.6
|
||||
org.mockito:mockito-core:3.7.7
|
||||
org.objenesis:objenesis:3.1
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
org.ow2.asm:asm-analysis:6.0
|
||||
org.ow2.asm:asm-commons:6.0
|
||||
|
||||
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
com.google.oauth-client:google-oauth-client:1.27.0
|
||||
com.google.protobuf:protobuf-java-util:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.6.1
|
||||
com.google.protobuf:protobuf-java:3.13.0
|
||||
com.google.template:soy:2018-03-14
|
||||
com.google.truth.extensions:truth-java8-extension:1.0
|
||||
com.google.truth:truth:1.0
|
||||
@@ -50,8 +50,8 @@ javax.inject:javax.inject:1
|
||||
javax.validation:validation-api:1.0.0.GA
|
||||
joda-time:joda-time:2.9.2
|
||||
junit:junit:4.12
|
||||
net.bytebuddy:byte-buddy-agent:1.10.5
|
||||
net.bytebuddy:byte-buddy:1.10.5
|
||||
net.bytebuddy:byte-buddy-agent:1.10.19
|
||||
net.bytebuddy:byte-buddy:1.10.19
|
||||
org.apache.commons:commons-lang3:3.8.1
|
||||
org.apache.commons:commons-text:1.6
|
||||
org.apache.httpcomponents:httpclient:4.5.8
|
||||
@@ -66,8 +66,8 @@ org.junit.jupiter:junit-jupiter-engine:5.6.2
|
||||
org.junit.platform:junit-platform-commons:1.6.2
|
||||
org.junit.platform:junit-platform-engine:1.6.2
|
||||
org.junit:junit-bom:5.6.2
|
||||
org.mockito:mockito-core:3.3.3
|
||||
org.objenesis:objenesis:2.6
|
||||
org.mockito:mockito-core:3.7.7
|
||||
org.objenesis:objenesis:3.1
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
org.ow2.asm:asm-analysis:6.0
|
||||
org.ow2.asm:asm-commons:6.0
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
mock-maker-inline
|
||||
+3
-2
@@ -111,8 +111,9 @@ PROPERTIES = [
|
||||
|
||||
# Cloud SQL properties
|
||||
Property('dbServer',
|
||||
'A registry environment name (e.g., "alpha") or a host[:port] '
|
||||
'string'),
|
||||
'Sets the target database of a Flyway task. This may be a '
|
||||
'registry environment name (e.g., alpha) or the host[:port] '
|
||||
'of a database that accepts direct IP access.'),
|
||||
Property('dbName',
|
||||
'Database name to use in connection.',
|
||||
'postgres'),
|
||||
|
||||
@@ -207,6 +207,9 @@ PRESUBMITS = {
|
||||
"ForeignKeyIndex.java",
|
||||
"HistoryEntryDao.java",
|
||||
"JpaTransactionManagerImpl.java",
|
||||
# CriteriaQueryBuilder is a false positive
|
||||
"CriteriaQueryBuilder.java",
|
||||
"RdapSearchActionBase.java",
|
||||
},
|
||||
):
|
||||
"The first String parameter to EntityManager.create(Native)Query "
|
||||
|
||||
@@ -76,6 +76,9 @@ def fragileTestPatterns = [
|
||||
"google/registry/model/tmch/ClaimsListShardTest.*",
|
||||
// Creates large object (64MBytes), occasionally throws OOM error.
|
||||
"google/registry/model/server/KmsSecretRevisionTest.*",
|
||||
// Changes cache timeouts and for some reason appears to have contention
|
||||
// with other tests.
|
||||
"google/registry/whois/WhoisCommandFactoryTest.*",
|
||||
] + dockerIncompatibleTestPatterns
|
||||
|
||||
sourceSets {
|
||||
@@ -344,6 +347,8 @@ dependencies {
|
||||
jaxb deps['com.sun.xml.bind:jaxb-osgi']
|
||||
|
||||
// Dependency needed for soy to java compilation.
|
||||
soy deps['com.google.inject:guice']
|
||||
soy deps['com.google.protobuf:protobuf-java']
|
||||
soy deps['com.google.template:soy']
|
||||
|
||||
// Dependencies needed for compiling stylesheets to javascript
|
||||
|
||||
@@ -3,19 +3,24 @@
|
||||
# This file is expected to be part of source control.
|
||||
aopalliance:aopalliance:1.0
|
||||
args4j:args4j:2.0.23
|
||||
com.google.code.findbugs:jsr305:2.0.3
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.code.gson:gson:2.7
|
||||
com.google.common.html.types:types:1.0.4
|
||||
com.google.guava:guava:21.0
|
||||
com.google.errorprone:error_prone_annotations:2.3.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.gwt:gwt-user:2.8.0-beta1
|
||||
com.google.inject.extensions:guice-multibindings:4.1.0
|
||||
com.google.inject:guice:4.1.0
|
||||
com.google.protobuf:protobuf-java:3.3.0
|
||||
com.google.inject:guice:5.0.1
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
com.google.protobuf:protobuf-java:3.13.0
|
||||
com.google.template:soy:2018-03-14
|
||||
com.ibm.icu:icu4j:57.1
|
||||
javax.annotation:jsr250-api:1.0
|
||||
javax.inject:javax.inject:1
|
||||
javax.validation:validation-api:1.0.0.GA
|
||||
org.checkerframework:checker-qual:3.5.0
|
||||
org.json:json:20160212
|
||||
org.ow2.asm:asm-analysis:6.0
|
||||
org.ow2.asm:asm-commons:6.0
|
||||
|
||||
@@ -198,8 +198,8 @@ javax.xml.bind:jaxb-api:2.3.1
|
||||
jline:jline:1.0
|
||||
joda-time:joda-time:2.10.5
|
||||
junit:junit:4.13.1
|
||||
net.bytebuddy:byte-buddy-agent:1.10.5
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.bytebuddy:byte-buddy-agent:1.10.19
|
||||
net.bytebuddy:byte-buddy:1.10.19
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
org.apache.beam:beam-model-fn-execution:2.27.0
|
||||
@@ -273,11 +273,11 @@ org.junit.platform:junit-platform-runner:1.7.0
|
||||
org.junit.platform:junit-platform-suite-api:1.7.0
|
||||
org.junit:junit-bom:5.7.0
|
||||
org.jvnet.staxex:stax-ex:1.8
|
||||
org.mockito:mockito-core:3.3.3
|
||||
org.mockito:mockito-junit-jupiter:3.3.3
|
||||
org.mockito:mockito-core:3.7.7
|
||||
org.mockito:mockito-junit-jupiter:3.7.7
|
||||
org.mortbay.jetty:jetty-util:6.1.26
|
||||
org.mortbay.jetty:jetty:6.1.26
|
||||
org.objenesis:objenesis:2.6
|
||||
org.objenesis:objenesis:3.1
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
org.ow2.asm:asm-analysis:8.0.1
|
||||
org.ow2.asm:asm-commons:7.1
|
||||
|
||||
@@ -193,8 +193,8 @@ javax.xml.bind:jaxb-api:2.3.1
|
||||
jline:jline:1.0
|
||||
joda-time:joda-time:2.10.5
|
||||
junit:junit:4.13.1
|
||||
net.bytebuddy:byte-buddy-agent:1.10.5
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.bytebuddy:byte-buddy-agent:1.10.19
|
||||
net.bytebuddy:byte-buddy:1.10.19
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
org.apache.beam:beam-model-fn-execution:2.27.0
|
||||
@@ -267,11 +267,11 @@ org.junit.platform:junit-platform-runner:1.7.0
|
||||
org.junit.platform:junit-platform-suite-api:1.7.0
|
||||
org.junit:junit-bom:5.7.0
|
||||
org.jvnet.staxex:stax-ex:1.8
|
||||
org.mockito:mockito-core:3.3.3
|
||||
org.mockito:mockito-junit-jupiter:3.3.3
|
||||
org.mockito:mockito-core:3.7.7
|
||||
org.mockito:mockito-junit-jupiter:3.7.7
|
||||
org.mortbay.jetty:jetty-util:6.1.26
|
||||
org.mortbay.jetty:jetty:6.1.26
|
||||
org.objenesis:objenesis:2.6
|
||||
org.objenesis:objenesis:3.1
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
org.ow2.asm:asm-analysis:8.0.1
|
||||
org.ow2.asm:asm-commons:7.1
|
||||
|
||||
@@ -208,8 +208,8 @@ jline:jline:1.0
|
||||
joda-time:joda-time:2.10.5
|
||||
junit:junit:4.13.1
|
||||
net.arnx:nashorn-promise:0.1.1
|
||||
net.bytebuddy:byte-buddy-agent:1.10.5
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.bytebuddy:byte-buddy-agent:1.10.19
|
||||
net.bytebuddy:byte-buddy:1.10.19
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
org.apache.beam:beam-model-fn-execution:2.27.0
|
||||
@@ -285,11 +285,11 @@ org.junit.platform:junit-platform-runner:1.7.0
|
||||
org.junit.platform:junit-platform-suite-api:1.7.0
|
||||
org.junit:junit-bom:5.7.0
|
||||
org.jvnet.staxex:stax-ex:1.8
|
||||
org.mockito:mockito-core:3.3.3
|
||||
org.mockito:mockito-junit-jupiter:3.3.3
|
||||
org.mockito:mockito-core:3.7.7
|
||||
org.mockito:mockito-junit-jupiter:3.7.7
|
||||
org.mortbay.jetty:jetty-util:6.1.26
|
||||
org.mortbay.jetty:jetty:6.1.26
|
||||
org.objenesis:objenesis:2.6
|
||||
org.objenesis:objenesis:3.1
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
org.ow2.asm:asm-analysis:8.0.1
|
||||
org.ow2.asm:asm-commons:7.1
|
||||
|
||||
@@ -208,8 +208,8 @@ jline:jline:1.0
|
||||
joda-time:joda-time:2.10.5
|
||||
junit:junit:4.13.1
|
||||
net.arnx:nashorn-promise:0.1.1
|
||||
net.bytebuddy:byte-buddy-agent:1.10.5
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.bytebuddy:byte-buddy-agent:1.10.19
|
||||
net.bytebuddy:byte-buddy:1.10.19
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
org.apache.beam:beam-model-fn-execution:2.27.0
|
||||
@@ -285,11 +285,11 @@ org.junit.platform:junit-platform-runner:1.7.0
|
||||
org.junit.platform:junit-platform-suite-api:1.7.0
|
||||
org.junit:junit-bom:5.7.0
|
||||
org.jvnet.staxex:stax-ex:1.8
|
||||
org.mockito:mockito-core:3.3.3
|
||||
org.mockito:mockito-junit-jupiter:3.3.3
|
||||
org.mockito:mockito-core:3.7.7
|
||||
org.mockito:mockito-junit-jupiter:3.7.7
|
||||
org.mortbay.jetty:jetty-util:6.1.26
|
||||
org.mortbay.jetty:jetty:6.1.26
|
||||
org.objenesis:objenesis:2.6
|
||||
org.objenesis:objenesis:3.1
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
org.ow2.asm:asm-analysis:8.0.1
|
||||
org.ow2.asm:asm-commons:7.1
|
||||
|
||||
@@ -104,8 +104,9 @@ public class DeleteExpiredDomainsAction implements Runnable {
|
||||
runLocked();
|
||||
response.setStatus(SC_OK);
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log("Errored out during execution.");
|
||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
response.setPayload("Encountered error; see GCP logs for full details.");
|
||||
response.setPayload(String.format("Errored out with cause: %s", e));
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -280,7 +280,7 @@
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/icannReportingUpload]]></url>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/icannReportingUpload&runInEmpty]]></url>
|
||||
<description>
|
||||
Checks if the monthly ICANN reports have been successfully uploaded. If they have not, attempts to upload them again.
|
||||
Most of the time, this job should not do anything since the uploads are triggered when the reports are staged.
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
@@ -141,7 +142,7 @@ public class ExportPremiumTermsAction implements Runnable {
|
||||
PremiumListDualDao.exists(premiumListName), "Could not load premium list for " + tld);
|
||||
SortedSet<String> premiumTerms =
|
||||
Streams.stream(PremiumListDualDao.loadAllPremiumListEntries(premiumListName))
|
||||
.map(entry -> Joiner.on(",").join(entry.getLabel(), entry.getValue()))
|
||||
.map(PremiumListEntry::toString)
|
||||
.collect(ImmutableSortedSet.toImmutableSortedSet(String::compareTo));
|
||||
|
||||
return Joiner.on("\n")
|
||||
|
||||
@@ -18,12 +18,11 @@ import static com.google.common.collect.Sets.intersection;
|
||||
import static google.registry.model.EppResourceUtils.getLinkedDomainKeys;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.index.ForeignKeyIndex.loadAndGetKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.flows.EppException.AuthorizationErrorException;
|
||||
import google.registry.flows.EppException.InvalidAuthorizationInformationErrorException;
|
||||
import google.registry.flows.EppException.ObjectDoesNotExistException;
|
||||
@@ -185,16 +184,15 @@ public final class ResourceFlowUtils {
|
||||
return;
|
||||
}
|
||||
// The roid should match one of the contacts.
|
||||
Optional<Key<ContactResource>> foundContact =
|
||||
Optional<VKey<ContactResource>> foundContact =
|
||||
domain.getReferencedContacts().stream()
|
||||
.map(VKey::getOfyKey)
|
||||
.filter(key -> key.getName().equals(authRepoId))
|
||||
.filter(key -> key.getOfyKey().getName().equals(authRepoId))
|
||||
.findFirst();
|
||||
if (!foundContact.isPresent()) {
|
||||
throw new BadAuthInfoForResourceException();
|
||||
}
|
||||
// Check the authInfo against the contact.
|
||||
verifyAuthInfo(authInfo, ofy().load().key(foundContact.get()).now());
|
||||
verifyAuthInfo(authInfo, transactIfJpaTm(() -> tm().loadByKey(foundContact.get())));
|
||||
}
|
||||
|
||||
/** Check that the given {@link AuthInfo} is valid for the given contact. */
|
||||
|
||||
@@ -25,6 +25,9 @@ import static com.google.common.collect.Iterables.any;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static com.google.common.collect.Sets.intersection;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.model.DatabaseMigrationUtils.getPrimaryDatabase;
|
||||
import static google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabase.DATASTORE;
|
||||
import static google.registry.model.common.DatabaseTransitionSchedule.TransitionId.REPLAYED_ENTITIES;
|
||||
import static google.registry.model.domain.DomainBase.MAX_REGISTRATION_YEARS;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.model.registry.Registries.findTldForName;
|
||||
@@ -38,6 +41,7 @@ import static google.registry.model.registry.label.ReservationType.FULLY_BLOCKED
|
||||
import static google.registry.model.registry.label.ReservationType.NAME_COLLISION;
|
||||
import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_ANCHOR_TENANT;
|
||||
import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_SPECIFIC_USE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
@@ -88,6 +92,7 @@ import google.registry.model.domain.DomainCommand.Create;
|
||||
import google.registry.model.domain.DomainCommand.CreateOrUpdate;
|
||||
import google.registry.model.domain.DomainCommand.InvalidReferencesException;
|
||||
import google.registry.model.domain.DomainCommand.Update;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.ForeignKeyedDesignatedContact;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.fee.BaseFee;
|
||||
@@ -355,22 +360,23 @@ public class DomainFlowUtils {
|
||||
|
||||
static void validateNoDuplicateContacts(Set<DesignatedContact> contacts)
|
||||
throws ParameterValuePolicyErrorException {
|
||||
ImmutableMultimap<Type, Key<ContactResource>> contactsByType =
|
||||
ImmutableMultimap<Type, VKey<ContactResource>> contactsByType =
|
||||
contacts.stream()
|
||||
.collect(
|
||||
toImmutableSetMultimap(
|
||||
DesignatedContact::getType, contact -> contact.getContactKey().getOfyKey()));
|
||||
DesignatedContact::getType, contact -> contact.getContactKey()));
|
||||
|
||||
// If any contact type has multiple contacts:
|
||||
if (contactsByType.asMap().values().stream().anyMatch(v -> v.size() > 1)) {
|
||||
// Find the duplicates.
|
||||
Map<Type, Collection<Key<ContactResource>>> dupeKeysMap =
|
||||
Map<Type, Collection<VKey<ContactResource>>> dupeKeysMap =
|
||||
Maps.filterEntries(contactsByType.asMap(), e -> e.getValue().size() > 1);
|
||||
ImmutableList<Key<ContactResource>> dupeKeys =
|
||||
ImmutableList<VKey<ContactResource>> dupeKeys =
|
||||
dupeKeysMap.values().stream().flatMap(Collection::stream).collect(toImmutableList());
|
||||
// Load the duplicates in one batch.
|
||||
Map<Key<ContactResource>, ContactResource> dupeContacts = ofy().load().keys(dupeKeys);
|
||||
ImmutableMultimap.Builder<Type, Key<ContactResource>> typesMap =
|
||||
Map<VKey<? extends ContactResource>, ContactResource> dupeContacts =
|
||||
tm().loadByKeys(dupeKeys);
|
||||
ImmutableMultimap.Builder<Type, VKey<ContactResource>> typesMap =
|
||||
new ImmutableMultimap.Builder<>();
|
||||
dupeKeysMap.forEach(typesMap::putAll);
|
||||
// Create an error message showing the type and contact IDs of the duplicates.
|
||||
@@ -537,13 +543,13 @@ public class DomainFlowUtils {
|
||||
// If the resultant autorenew poll message would have no poll messages to deliver, then just
|
||||
// delete it. Otherwise save it with the new end time.
|
||||
if (isAtOrAfter(updatedAutorenewPollMessage.getEventTime(), newEndTime)) {
|
||||
autorenewPollMessage.ifPresent(autorenew -> ofy().delete().entity(autorenew));
|
||||
autorenewPollMessage.ifPresent(autorenew -> tm().delete(autorenew));
|
||||
} else {
|
||||
ofy().save().entity(updatedAutorenewPollMessage);
|
||||
tm().put(updatedAutorenewPollMessage);
|
||||
}
|
||||
|
||||
Recurring recurring = tm().loadByKey(domain.getAutorenewBillingEvent());
|
||||
ofy().save().entity(recurring.asBuilder().setRecurrenceEndTime(newEndTime).build());
|
||||
tm().put(recurring.asBuilder().setRecurrenceEndTime(newEndTime).build());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1045,18 +1051,11 @@ public class DomainFlowUtils {
|
||||
Duration maxSearchPeriod,
|
||||
final ImmutableSet<TransactionReportField> cancelableFields) {
|
||||
|
||||
List<HistoryEntry> recentHistoryEntries =
|
||||
ofy()
|
||||
.load()
|
||||
.type(HistoryEntry.class)
|
||||
.ancestor(domainBase)
|
||||
.filter("modificationTime >=", now.minus(maxSearchPeriod))
|
||||
.order("modificationTime")
|
||||
.list();
|
||||
Optional<HistoryEntry> entryToCancel =
|
||||
List<? extends HistoryEntry> recentHistoryEntries =
|
||||
findRecentHistoryEntries(domainBase, now, maxSearchPeriod);
|
||||
Optional<? extends HistoryEntry> entryToCancel =
|
||||
Streams.findLast(
|
||||
recentHistoryEntries
|
||||
.stream()
|
||||
recentHistoryEntries.stream()
|
||||
.filter(
|
||||
historyEntry -> {
|
||||
// Look for add and renew transaction records that have yet to be reported
|
||||
@@ -1082,6 +1081,28 @@ public class DomainFlowUtils {
|
||||
return recordsBuilder.build();
|
||||
}
|
||||
|
||||
private static List<? extends HistoryEntry> findRecentHistoryEntries(
|
||||
DomainBase domainBase, DateTime now, Duration maxSearchPeriod) {
|
||||
if (getPrimaryDatabase(REPLAYED_ENTITIES).equals(DATASTORE)) {
|
||||
return ofy()
|
||||
.load()
|
||||
.type(HistoryEntry.class)
|
||||
.ancestor(domainBase)
|
||||
.filter("modificationTime >=", now.minus(maxSearchPeriod))
|
||||
.order("modificationTime")
|
||||
.list();
|
||||
} else {
|
||||
return jpaTm()
|
||||
.getEntityManager()
|
||||
.createQuery(
|
||||
"FROM DomainHistory WHERE modificationTime >= :beginning "
|
||||
+ "ORDER BY modificationTime ASC",
|
||||
DomainHistory.class)
|
||||
.setParameter("beginning", now.minus(maxSearchPeriod))
|
||||
.getResultList();
|
||||
}
|
||||
}
|
||||
|
||||
/** Resource linked to this domain does not exist. */
|
||||
static class LinkedResourcesDoNotExistException extends ObjectDoesNotExistException {
|
||||
public LinkedResourcesDoNotExistException(Class<?> type, ImmutableSet<String> resourceIds) {
|
||||
|
||||
@@ -25,7 +25,6 @@ import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurr
|
||||
import static google.registry.flows.domain.DomainTransferUtils.createGainingTransferPollMessage;
|
||||
import static google.registry.flows.domain.DomainTransferUtils.createTransferResponse;
|
||||
import static google.registry.model.ResourceTransferUtils.denyPendingTransfer;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.TRANSFER_NACKED;
|
||||
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.TRANSFER_SUCCESSFUL;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
@@ -41,7 +40,6 @@ import google.registry.flows.FlowModule.Superuser;
|
||||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.TransactionalFlow;
|
||||
import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
@@ -102,11 +100,11 @@ public final class DomainTransferRejectFlow implements TransactionalFlow {
|
||||
}
|
||||
DomainBase newDomain =
|
||||
denyPendingTransfer(existingDomain, TransferStatus.CLIENT_REJECTED, now, clientId);
|
||||
ofy().save().<ImmutableObject>entities(
|
||||
newDomain,
|
||||
historyEntry,
|
||||
createGainingTransferPollMessage(
|
||||
targetId, newDomain.getTransferData(), null, historyEntry));
|
||||
tm().putAll(
|
||||
newDomain,
|
||||
historyEntry,
|
||||
createGainingTransferPollMessage(
|
||||
targetId, newDomain.getTransferData(), null, historyEntry));
|
||||
// Reopen the autorenew event and poll message that we closed for the implicit transfer. This
|
||||
// may end up recreating the poll message if it was deleted upon the transfer request.
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, END_OF_TIME);
|
||||
|
||||
@@ -52,6 +52,7 @@ import google.registry.model.tmch.ClaimsListShard;
|
||||
import google.registry.model.tmch.ClaimsListShard.ClaimsListRevision;
|
||||
import google.registry.model.tmch.ClaimsListShard.ClaimsListSingleton;
|
||||
import google.registry.model.tmch.TmchCrl;
|
||||
import google.registry.schema.replay.LastSqlTransaction;
|
||||
|
||||
/** Sets of classes of the Objectify-registered entities in use throughout the model. */
|
||||
public final class EntityClasses {
|
||||
@@ -90,6 +91,7 @@ public final class EntityClasses {
|
||||
HostResource.class,
|
||||
KmsSecret.class,
|
||||
KmsSecretRevision.class,
|
||||
LastSqlTransaction.class,
|
||||
Lock.class,
|
||||
PollMessage.class,
|
||||
PollMessage.Autorenew.class,
|
||||
|
||||
@@ -118,9 +118,7 @@ public final class ResourceTransferUtils {
|
||||
if (resource.getStatusValues().contains(StatusValue.PENDING_TRANSFER)) {
|
||||
TransferData oldTransferData = resource.getTransferData();
|
||||
tm().delete(oldTransferData.getServerApproveEntities());
|
||||
ofy()
|
||||
.save()
|
||||
.entity(
|
||||
tm().put(
|
||||
new PollMessage.OneTime.Builder()
|
||||
.setClientId(oldTransferData.getGainingClientId())
|
||||
.setEventTime(now)
|
||||
|
||||
@@ -62,6 +62,8 @@ public class DatabaseTransitionSchedule extends ImmutableObject implements Datas
|
||||
DOMAIN_LABEL_LISTS,
|
||||
/** The schedule for the migration of the {@link SignedMarkRevocationList} entity. */
|
||||
SIGNED_MARK_REVOCATION_LIST,
|
||||
/** The schedule for all asynchronously-replayed entities, ones not dually-written. */
|
||||
REPLAYED_ENTITIES,
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -131,6 +131,11 @@ public class DatastoreTransactionManager implements TransactionManager {
|
||||
saveEntity(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Object... entities) {
|
||||
syncIfTransactionless(getOfy().save().entities(entities));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(ImmutableCollection<?> entities) {
|
||||
syncIfTransactionless(getOfy().save().entities(entities));
|
||||
|
||||
@@ -41,9 +41,9 @@ public class EntityWritePriorities {
|
||||
*/
|
||||
static final ImmutableMap<String, Integer> CLASS_PRIORITIES =
|
||||
ImmutableMap.of(
|
||||
"ContactResource", -15,
|
||||
"HistoryEntry", -10,
|
||||
"AllocationToken", -9,
|
||||
"ContactResource", 5,
|
||||
"DomainBase", 10);
|
||||
|
||||
// The beginning of the range of priority numbers reserved for delete. This must be greater than
|
||||
|
||||
@@ -116,7 +116,7 @@ public class Ofy {
|
||||
return ofy().getTransaction() != null;
|
||||
}
|
||||
|
||||
void assertInTransaction() {
|
||||
public void assertInTransaction() {
|
||||
checkState(inTransaction(), "Must be called in a transaction");
|
||||
}
|
||||
|
||||
|
||||
@@ -359,8 +359,13 @@ public abstract class PollMessage extends ImmutableObject
|
||||
}
|
||||
|
||||
/** Converts an unspecialized VKey<PollMessage> to a VKey of the derived class. */
|
||||
public static @Nullable VKey<OneTime> convertVKey(@Nullable VKey<OneTime> key) {
|
||||
return key == null ? null : VKey.create(OneTime.class, key.getSqlKey(), key.getOfyKey());
|
||||
public static @Nullable VKey<OneTime> convertVKey(@Nullable VKey<? extends PollMessage> key) {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
Key<OneTime> ofyKey =
|
||||
Key.create(key.getOfyKey().getParent(), OneTime.class, key.getOfyKey().getId());
|
||||
return VKey.create(OneTime.class, key.getSqlKey(), ofyKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -383,6 +388,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||
@OnLoad
|
||||
void onLoad() {
|
||||
super.onLoad();
|
||||
// Take the Objectify-specific fields and map them to the SQL-specific fields, if applicable
|
||||
if (!isNullOrEmpty(contactPendingActionNotificationResponses)) {
|
||||
pendingActionNotificationResponse = contactPendingActionNotificationResponses.get(0);
|
||||
}
|
||||
@@ -390,36 +396,70 @@ public abstract class PollMessage extends ImmutableObject
|
||||
contactId = contactTransferResponses.get(0).getContactId();
|
||||
transferResponse = contactTransferResponses.get(0);
|
||||
}
|
||||
if (!isNullOrEmpty(domainPendingActionNotificationResponses)) {
|
||||
pendingActionNotificationResponse = domainPendingActionNotificationResponses.get(0);
|
||||
}
|
||||
if (!isNullOrEmpty(domainTransferResponses)) {
|
||||
fullyQualifiedDomainName = domainTransferResponses.get(0).getFullyQualifiedDomainName();
|
||||
transferResponse = domainTransferResponses.get(0);
|
||||
extendedRegistrationExpirationTime =
|
||||
domainTransferResponses.get(0).getExtendedRegistrationExpirationTime();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostLoad
|
||||
void postLoad() {
|
||||
super.postLoad();
|
||||
// Take the SQL-specific fields and map them to the Objectify-specific fields, if applicable
|
||||
if (pendingActionNotificationResponse != null) {
|
||||
contactPendingActionNotificationResponses =
|
||||
ImmutableList.of(
|
||||
ContactPendingActionNotificationResponse.create(
|
||||
pendingActionNotificationResponse.nameOrId.value,
|
||||
pendingActionNotificationResponse.getActionResult(),
|
||||
pendingActionNotificationResponse.getTrid(),
|
||||
pendingActionNotificationResponse.processedDate));
|
||||
if (contactId != null) {
|
||||
contactPendingActionNotificationResponses =
|
||||
ImmutableList.of(
|
||||
ContactPendingActionNotificationResponse.create(
|
||||
pendingActionNotificationResponse.nameOrId.value,
|
||||
pendingActionNotificationResponse.getActionResult(),
|
||||
pendingActionNotificationResponse.getTrid(),
|
||||
pendingActionNotificationResponse.processedDate));
|
||||
} else if (fullyQualifiedDomainName != null) {
|
||||
domainPendingActionNotificationResponses =
|
||||
ImmutableList.of(
|
||||
DomainPendingActionNotificationResponse.create(
|
||||
pendingActionNotificationResponse.nameOrId.value,
|
||||
pendingActionNotificationResponse.getActionResult(),
|
||||
pendingActionNotificationResponse.getTrid(),
|
||||
pendingActionNotificationResponse.processedDate));
|
||||
}
|
||||
}
|
||||
if (contactId != null && transferResponse != null) {
|
||||
// The transferResponse is currently an unspecialized TransferResponse instance, create a
|
||||
// ContactTransferResponse so that the value is consistently specialized and store it in the
|
||||
// list representation for datastore.
|
||||
transferResponse =
|
||||
new ContactTransferResponse.Builder()
|
||||
.setContactId(contactId)
|
||||
.setGainingClientId(transferResponse.getGainingClientId())
|
||||
.setLosingClientId(transferResponse.getLosingClientId())
|
||||
.setTransferStatus(transferResponse.getTransferStatus())
|
||||
.setTransferRequestTime(transferResponse.getTransferRequestTime())
|
||||
.setPendingTransferExpirationTime(
|
||||
transferResponse.getPendingTransferExpirationTime())
|
||||
.build();
|
||||
contactTransferResponses = ImmutableList.of((ContactTransferResponse) transferResponse);
|
||||
if (transferResponse != null) {
|
||||
// The transferResponse is currently an unspecialized TransferResponse instance, create the
|
||||
// appropriate subclass so that the value is consistently specialized
|
||||
if (contactId != null) {
|
||||
transferResponse =
|
||||
new ContactTransferResponse.Builder()
|
||||
.setContactId(contactId)
|
||||
.setGainingClientId(transferResponse.getGainingClientId())
|
||||
.setLosingClientId(transferResponse.getLosingClientId())
|
||||
.setTransferStatus(transferResponse.getTransferStatus())
|
||||
.setTransferRequestTime(transferResponse.getTransferRequestTime())
|
||||
.setPendingTransferExpirationTime(
|
||||
transferResponse.getPendingTransferExpirationTime())
|
||||
.build();
|
||||
contactTransferResponses = ImmutableList.of((ContactTransferResponse) transferResponse);
|
||||
} else if (fullyQualifiedDomainName != null) {
|
||||
transferResponse =
|
||||
new DomainTransferResponse.Builder()
|
||||
.setFullyQualifiedDomainName(fullyQualifiedDomainName)
|
||||
.setGainingClientId(transferResponse.getGainingClientId())
|
||||
.setLosingClientId(transferResponse.getLosingClientId())
|
||||
.setTransferStatus(transferResponse.getTransferStatus())
|
||||
.setTransferRequestTime(transferResponse.getTransferRequestTime())
|
||||
.setPendingTransferExpirationTime(
|
||||
transferResponse.getPendingTransferExpirationTime())
|
||||
.setExtendedRegistrationExpirationTime(extendedRegistrationExpirationTime)
|
||||
.build();
|
||||
domainTransferResponses = ImmutableList.of((DomainTransferResponse) transferResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,10 +481,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||
.filter(ContactPendingActionNotificationResponse.class::isInstance)
|
||||
.map(ContactPendingActionNotificationResponse.class::cast)
|
||||
.collect(toImmutableList()));
|
||||
if (getInstance().contactPendingActionNotificationResponses != null) {
|
||||
getInstance().pendingActionNotificationResponse =
|
||||
getInstance().contactPendingActionNotificationResponses.get(0);
|
||||
}
|
||||
|
||||
getInstance().contactTransferResponses =
|
||||
forceEmptyToNull(
|
||||
responseData
|
||||
@@ -452,10 +489,6 @@ public abstract class PollMessage extends ImmutableObject
|
||||
.filter(ContactTransferResponse.class::isInstance)
|
||||
.map(ContactTransferResponse.class::cast)
|
||||
.collect(toImmutableList()));
|
||||
if (getInstance().contactTransferResponses != null) {
|
||||
getInstance().contactId = getInstance().contactTransferResponses.get(0).getContactId();
|
||||
getInstance().transferResponse = getInstance().contactTransferResponses.get(0);
|
||||
}
|
||||
|
||||
getInstance().domainPendingActionNotificationResponses =
|
||||
forceEmptyToNull(
|
||||
@@ -471,13 +504,36 @@ public abstract class PollMessage extends ImmutableObject
|
||||
.filter(DomainTransferResponse.class::isInstance)
|
||||
.map(DomainTransferResponse.class::cast)
|
||||
.collect(toImmutableList()));
|
||||
|
||||
getInstance().hostPendingActionNotificationResponses =
|
||||
forceEmptyToNull(
|
||||
responseData
|
||||
.stream()
|
||||
responseData.stream()
|
||||
.filter(HostPendingActionNotificationResponse.class::isInstance)
|
||||
.map(HostPendingActionNotificationResponse.class::cast)
|
||||
.collect(toImmutableList()));
|
||||
|
||||
// Set the generic pending-action field as appropriate
|
||||
if (getInstance().contactPendingActionNotificationResponses != null) {
|
||||
getInstance().pendingActionNotificationResponse =
|
||||
getInstance().contactPendingActionNotificationResponses.get(0);
|
||||
} else if (getInstance().domainPendingActionNotificationResponses != null) {
|
||||
getInstance().pendingActionNotificationResponse =
|
||||
getInstance().domainPendingActionNotificationResponses.get(0);
|
||||
} else if (getInstance().hostPendingActionNotificationResponses != null) {
|
||||
getInstance().pendingActionNotificationResponse =
|
||||
getInstance().hostPendingActionNotificationResponses.get(0);
|
||||
}
|
||||
// Set the generic transfer response field as appropriate
|
||||
if (getInstance().contactTransferResponses != null) {
|
||||
getInstance().contactId = getInstance().contactTransferResponses.get(0).getContactId();
|
||||
getInstance().transferResponse = getInstance().contactTransferResponses.get(0);
|
||||
} else if (getInstance().domainTransferResponses != null) {
|
||||
getInstance().fullyQualifiedDomainName =
|
||||
getInstance().domainTransferResponses.get(0).getFullyQualifiedDomainName();
|
||||
getInstance().transferResponse = getInstance().domainTransferResponses.get(0);
|
||||
getInstance().extendedRegistrationExpirationTime =
|
||||
getInstance().domainTransferResponses.get(0).getExtendedRegistrationExpirationTime();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -518,8 +574,13 @@ public abstract class PollMessage extends ImmutableObject
|
||||
}
|
||||
|
||||
/** Converts an unspecialized VKey<PollMessage> to a VKey of the derived class. */
|
||||
public static @Nullable VKey<Autorenew> convertVKey(VKey<Autorenew> key) {
|
||||
return key == null ? null : VKey.create(Autorenew.class, key.getSqlKey(), key.getOfyKey());
|
||||
public static @Nullable VKey<Autorenew> convertVKey(VKey<? extends PollMessage> key) {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
Key<Autorenew> ofyKey =
|
||||
Key.create(key.getOfyKey().getParent(), Autorenew.class, key.getOfyKey().getId());
|
||||
return VKey.create(Autorenew.class, key.getSqlKey(), ofyKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -208,6 +208,12 @@ public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.Pr
|
||||
return price;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// Don't include the comment so that we can use this when exporting the premium list
|
||||
return String.format("%s,%s", label, price);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
|
||||
package google.registry.model.transfer;
|
||||
|
||||
import static google.registry.util.CollectionUtils.forceEmptyToNull;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.AlsoLoad;
|
||||
import com.googlecode.objectify.annotation.Embed;
|
||||
@@ -34,6 +37,7 @@ import javax.persistence.AttributeOverrides;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.PostLoad;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Transfer data for domain. */
|
||||
@@ -214,6 +218,28 @@ public class DomainTransferData extends TransferData<DomainTransferData.Builder>
|
||||
return serverApproveAutorenewPollMessageHistoryId;
|
||||
}
|
||||
|
||||
@PostLoad
|
||||
@Override
|
||||
void postLoad() {
|
||||
// The superclass's serverApproveEntities should include the billing events if present
|
||||
super.postLoad();
|
||||
ImmutableSet.Builder<VKey<? extends TransferServerApproveEntity>> serverApproveEntitiesBuilder =
|
||||
new ImmutableSet.Builder<>();
|
||||
if (serverApproveEntities != null) {
|
||||
serverApproveEntitiesBuilder.addAll(serverApproveEntities);
|
||||
}
|
||||
if (serverApproveBillingEvent != null) {
|
||||
serverApproveEntitiesBuilder.add(serverApproveBillingEvent);
|
||||
}
|
||||
if (serverApproveAutorenewEvent != null) {
|
||||
serverApproveEntitiesBuilder.add(serverApproveAutorenewEvent);
|
||||
}
|
||||
if (serverApproveAutorenewPollMessage != null) {
|
||||
serverApproveEntitiesBuilder.add(serverApproveAutorenewPollMessage);
|
||||
}
|
||||
serverApproveEntities = forceEmptyToNull(serverApproveEntitiesBuilder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return EMPTY.equals(this);
|
||||
|
||||
@@ -167,7 +167,7 @@ public abstract class TransferData<
|
||||
return;
|
||||
}
|
||||
Key<? extends EppResource> eppKey;
|
||||
if (getClass().equals(DomainBase.class)) {
|
||||
if (getClass().equals(DomainTransferData.class)) {
|
||||
eppKey = Key.create(DomainBase.class, repoId);
|
||||
} else {
|
||||
eppKey = Key.create(ContactResource.class, repoId);
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.transaction;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.Collection;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.Order;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
/**
|
||||
* An extension of {@link CriteriaQuery} that uses a Builder-style pattern when adding "WHERE"
|
||||
* and/or "ORDER BY" clauses.
|
||||
*
|
||||
* <p>{@link CriteriaQuery}, as is, requires that all clauses must be passed in at once -- if one
|
||||
* calls "WHERE" multiple times, the later call overwrites the earlier call.
|
||||
*/
|
||||
public class CriteriaQueryBuilder<T> {
|
||||
|
||||
/** Functional interface that defines the 'where' operator, e.g. {@link CriteriaBuilder#equal}. */
|
||||
public interface WhereClause<U> {
|
||||
Predicate predicate(Expression<U> expression, U object);
|
||||
}
|
||||
|
||||
/** Functional interface that defines the order-by operator, e.g. {@link CriteriaBuilder#asc}. */
|
||||
public interface OrderByClause<U> {
|
||||
Order order(Expression<U> expression);
|
||||
}
|
||||
|
||||
private final CriteriaQuery<T> query;
|
||||
private final Root<T> root;
|
||||
private final ImmutableList.Builder<Predicate> predicates = new ImmutableList.Builder<>();
|
||||
private final ImmutableList.Builder<Order> orders = new ImmutableList.Builder<>();
|
||||
|
||||
private CriteriaQueryBuilder(CriteriaQuery<T> query, Root<T> root) {
|
||||
this.query = query;
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
/** Adds a WHERE clause to the query, given the specified operation, field, and value. */
|
||||
public <V> CriteriaQueryBuilder<T> where(WhereClause<V> whereClause, String fieldName, V value) {
|
||||
Expression<V> expression = root.get(fieldName);
|
||||
return where(whereClause.predicate(expression, value));
|
||||
}
|
||||
|
||||
/** Adds a WHERE clause to the query specifying that a value must be in the given collection. */
|
||||
public CriteriaQueryBuilder<T> whereFieldIsIn(String fieldName, Collection<?> values) {
|
||||
return where(root.get(fieldName).in(values));
|
||||
}
|
||||
|
||||
/** Orders the result by the given operation applied to the given field. */
|
||||
public <U> CriteriaQueryBuilder<T> orderBy(OrderByClause<U> orderByClause, String fieldName) {
|
||||
Expression<U> expression = root.get(fieldName);
|
||||
return orderBy(orderByClause.order(expression));
|
||||
}
|
||||
|
||||
/** Builds and returns the query, applying all WHERE and ORDER BY clauses at once. */
|
||||
public CriteriaQuery<T> build() {
|
||||
Predicate[] predicateArray = predicates.build().toArray(new Predicate[0]);
|
||||
return query.where(predicateArray).orderBy(orders.build());
|
||||
}
|
||||
|
||||
private CriteriaQueryBuilder<T> where(Predicate predicate) {
|
||||
predicates.add(predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
private CriteriaQueryBuilder<T> orderBy(Order order) {
|
||||
orders.add(order);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Creates a query builder that will SELECT from the given class. */
|
||||
public static <T> CriteriaQueryBuilder<T> create(Class<T> clazz) {
|
||||
CriteriaQuery<T> query = jpaTm().getEntityManager().getCriteriaBuilder().createQuery(clazz);
|
||||
Root<T> root = query.from(clazz);
|
||||
query = query.select(root);
|
||||
return new CriteriaQueryBuilder<>(query, root);
|
||||
}
|
||||
}
|
||||
+9
@@ -288,6 +288,15 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
transactionInfo.get().addUpdate(toPersist);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Object... entities) {
|
||||
checkArgumentNotNull(entities, "entities must be specified");
|
||||
assertInTransaction();
|
||||
for (Object entity : entities) {
|
||||
put(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(ImmutableCollection<?> entities) {
|
||||
checkArgumentNotNull(entities, "entities must be specified");
|
||||
|
||||
@@ -86,7 +86,7 @@ public class Transaction extends ImmutableObject implements Buildable {
|
||||
}
|
||||
}
|
||||
|
||||
static Transaction deserialize(byte[] serializedTransaction) throws IOException {
|
||||
public static Transaction deserialize(byte[] serializedTransaction) throws IOException {
|
||||
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(serializedTransaction));
|
||||
|
||||
// Verify that the data is what we expect.
|
||||
|
||||
@@ -34,9 +34,9 @@ public class TransactionEntity implements SqlEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
long id;
|
||||
private long id;
|
||||
|
||||
byte[] contents;
|
||||
private byte[] contents;
|
||||
|
||||
TransactionEntity() {}
|
||||
|
||||
@@ -48,4 +48,12 @@ public class TransactionEntity implements SqlEntity {
|
||||
public Optional<DatastoreEntity> toDatastoreEntity() {
|
||||
return Optional.empty(); // Not persisted in Datastore per se
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public byte[] getContents() {
|
||||
return contents;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,10 @@ public interface TransactionManager {
|
||||
/** Persists a new entity or update the existing entity in the database. */
|
||||
void put(Object entity);
|
||||
|
||||
/** Persists all new entities or update the existing entities in the database. */
|
||||
/** Persists all new entities or updates the existing entities in the database. */
|
||||
void putAll(Object... entities);
|
||||
|
||||
/** Persists all new entities or updates the existing entities in the database. */
|
||||
void putAll(ImmutableCollection<?> entities);
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.rdap;
|
||||
import static com.google.common.base.Charsets.UTF_8;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.net.HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Actions.getPathForAction;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
@@ -28,7 +29,10 @@ import com.google.common.net.MediaType;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.DatabaseMigrationUtils;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabase;
|
||||
import google.registry.model.common.DatabaseTransitionSchedule.TransitionId;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.rdap.RdapMetrics.EndpointType;
|
||||
import google.registry.rdap.RdapObjectClasses.ErrorResponse;
|
||||
@@ -256,4 +260,11 @@ public abstract class RdapActionBase implements Runnable {
|
||||
DateTime getRequestTime() {
|
||||
return rdapJsonFormatter.getRequestTime();
|
||||
}
|
||||
|
||||
static boolean isDatastore() {
|
||||
return tm().transact(
|
||||
() ->
|
||||
DatabaseMigrationUtils.getPrimaryDatabase(TransitionId.REPLAYED_ENTITIES)
|
||||
.equals(PrimaryDatabase.DATASTORE));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
package google.registry.rdap;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
import static google.registry.rdap.RdapUtils.getRegistrarByIanaIdentifier;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.HEAD;
|
||||
@@ -29,6 +31,9 @@ import com.google.common.primitives.Longs;
|
||||
import com.googlecode.objectify.cmd.Query;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import google.registry.rdap.RdapAuthorization.Role;
|
||||
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
|
||||
import google.registry.rdap.RdapMetrics.EndpointType;
|
||||
import google.registry.rdap.RdapMetrics.SearchType;
|
||||
@@ -112,7 +117,6 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
/** Parses the parameters and calls the appropriate search function. */
|
||||
@Override
|
||||
public EntitySearchResponse getSearchResponse(boolean isHeadRequest) {
|
||||
|
||||
// RDAP syntax example: /rdap/entities?fn=Bobby%20Joe*.
|
||||
if (Booleans.countTrue(fnParam.isPresent(), handleParam.isPresent()) != 1) {
|
||||
throw new BadRequestException("You must specify either fn=XXXX or handle=YYYY");
|
||||
@@ -214,9 +218,7 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
// Don't allow wildcard suffixes when searching for entities.
|
||||
if (partialStringQuery.getHasWildcard() && (partialStringQuery.getSuffix() != null)) {
|
||||
throw new UnprocessableEntityException(
|
||||
partialStringQuery.getHasWildcard()
|
||||
? "Suffixes not allowed in wildcard entity name searches"
|
||||
: "Suffixes not allowed when searching for deleted entities");
|
||||
"Suffixes not allowed in wildcard entity name searches");
|
||||
}
|
||||
// For wildcards, make sure the initial string is long enough, except in the special case of
|
||||
// searching for all registrars, where we aren't worried about inefficient searches.
|
||||
@@ -225,9 +227,7 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
&& (partialStringQuery.getInitialString().length()
|
||||
< RdapSearchPattern.MIN_INITIAL_STRING_LENGTH)) {
|
||||
throw new UnprocessableEntityException(
|
||||
partialStringQuery.getHasWildcard()
|
||||
? "Initial search string required in wildcard entity name searches"
|
||||
: "Initial search string required when searching for deleted entities");
|
||||
"Initial search string required in wildcard entity name searches");
|
||||
}
|
||||
// Get the registrar matches. If we have a registrar cursor, weed out registrars up to and
|
||||
// including the one we ended with last time. We can skip registrars if subtype is CONTACTS.
|
||||
@@ -262,18 +262,39 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
|| (cursorType == CursorType.REGISTRAR)) {
|
||||
resultSet = RdapResultSet.create(ImmutableList.of());
|
||||
} else {
|
||||
Query<ContactResource> query =
|
||||
queryItems(
|
||||
ContactResource.class,
|
||||
"searchName",
|
||||
partialStringQuery,
|
||||
cursorQueryString, // if we get this far, and there's a cursor, it must be a contact
|
||||
DeletedItemHandling.EXCLUDE,
|
||||
rdapResultSetMaxSize + 1);
|
||||
if (rdapAuthorization.role() != RdapAuthorization.Role.ADMINISTRATOR) {
|
||||
query = query.filter("currentSponsorClientId in", rdapAuthorization.clientIds());
|
||||
if (isDatastore()) {
|
||||
Query<ContactResource> query =
|
||||
queryItems(
|
||||
ContactResource.class,
|
||||
"searchName",
|
||||
partialStringQuery,
|
||||
cursorQueryString, // if we get here and there's a cursor, it must be a contact
|
||||
DeletedItemHandling.EXCLUDE,
|
||||
rdapResultSetMaxSize + 1);
|
||||
if (!rdapAuthorization.role().equals(Role.ADMINISTRATOR)) {
|
||||
query = query.filter("currentSponsorClientId in", rdapAuthorization.clientIds());
|
||||
}
|
||||
resultSet = getMatchingResources(query, false, rdapResultSetMaxSize + 1);
|
||||
} else {
|
||||
resultSet =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
CriteriaQueryBuilder<ContactResource> builder =
|
||||
queryItemsSql(
|
||||
ContactResource.class,
|
||||
"searchName",
|
||||
partialStringQuery,
|
||||
cursorQueryString,
|
||||
DeletedItemHandling.EXCLUDE);
|
||||
if (!rdapAuthorization.role().equals(Role.ADMINISTRATOR)) {
|
||||
builder =
|
||||
builder.whereFieldIsIn(
|
||||
"currentSponsorClientId", rdapAuthorization.clientIds());
|
||||
}
|
||||
return getMatchingResourcesSql(builder, false, rdapResultSetMaxSize + 1);
|
||||
});
|
||||
}
|
||||
resultSet = getMatchingResources(query, false, rdapResultSetMaxSize + 1);
|
||||
}
|
||||
}
|
||||
return makeSearchResults(resultSet, registrars, QueryType.FULL_NAME);
|
||||
@@ -303,15 +324,15 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
if (subtype == Subtype.REGISTRARS) {
|
||||
contactResourceList = ImmutableList.of();
|
||||
} else {
|
||||
ContactResource contactResource =
|
||||
ofy()
|
||||
.load()
|
||||
.type(ContactResource.class)
|
||||
.id(partialStringQuery.getInitialString())
|
||||
.now();
|
||||
Optional<ContactResource> contactResource =
|
||||
transactIfJpaTm(
|
||||
() ->
|
||||
tm().loadByKeyIfPresent(
|
||||
VKey.create(
|
||||
ContactResource.class, partialStringQuery.getInitialString())));
|
||||
contactResourceList =
|
||||
((contactResource != null) && shouldBeVisible(contactResource))
|
||||
? ImmutableList.of(contactResource)
|
||||
(contactResource.isPresent() && shouldBeVisible(contactResource.get()))
|
||||
? ImmutableList.of(contactResource.get())
|
||||
: ImmutableList.of();
|
||||
}
|
||||
ImmutableList<Registrar> registrarList;
|
||||
@@ -365,16 +386,31 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
if (subtype == Subtype.REGISTRARS) {
|
||||
contactResultSet = RdapResultSet.create(ImmutableList.of());
|
||||
} else {
|
||||
contactResultSet =
|
||||
getMatchingResources(
|
||||
queryItemsByKey(
|
||||
ContactResource.class,
|
||||
partialStringQuery,
|
||||
cursorQueryString,
|
||||
getDeletedItemHandling(),
|
||||
querySizeLimit),
|
||||
shouldIncludeDeleted(),
|
||||
querySizeLimit);
|
||||
if (isDatastore()) {
|
||||
contactResultSet =
|
||||
getMatchingResources(
|
||||
queryItemsByKey(
|
||||
ContactResource.class,
|
||||
partialStringQuery,
|
||||
cursorQueryString,
|
||||
getDeletedItemHandling(),
|
||||
querySizeLimit),
|
||||
shouldIncludeDeleted(),
|
||||
querySizeLimit);
|
||||
} else {
|
||||
contactResultSet =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
getMatchingResourcesSql(
|
||||
queryItemsByKeySql(
|
||||
ContactResource.class,
|
||||
partialStringQuery,
|
||||
cursorQueryString,
|
||||
getDeletedItemHandling()),
|
||||
shouldIncludeDeleted(),
|
||||
querySizeLimit));
|
||||
}
|
||||
}
|
||||
return makeSearchResults(contactResultSet, registrars, QueryType.HANDLE);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.rdap;
|
||||
|
||||
import static com.google.common.base.Charsets.UTF_8;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -24,6 +25,7 @@ import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.cmd.Query;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import google.registry.rdap.RdapMetrics.EndpointType;
|
||||
import google.registry.rdap.RdapMetrics.WildcardType;
|
||||
import google.registry.rdap.RdapSearchResults.BaseSearchResponse;
|
||||
@@ -40,8 +42,10 @@ import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
|
||||
/**
|
||||
* Base RDAP (new WHOIS) action for domain, nameserver and entity search requests.
|
||||
@@ -161,14 +165,63 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
|
||||
if (desiredRegistrar.isPresent()) {
|
||||
query = query.filter("currentSponsorClientId", desiredRegistrar.get());
|
||||
}
|
||||
if (!checkForVisibility) {
|
||||
return RdapResultSet.create(query.list());
|
||||
List<T> queryResult = query.list();
|
||||
if (checkForVisibility) {
|
||||
return filterResourcesByVisibility(queryResult, querySizeLimit);
|
||||
} else {
|
||||
return RdapResultSet.create(queryResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In Cloud SQL, builds and runs the given query, and checks for permissioning if necessary.
|
||||
*
|
||||
* @param builder a query builder that represents the various SELECT FROM, WHERE, ORDER BY and
|
||||
* (etc) clauses that make up this SQL query
|
||||
* @param checkForVisibility true if the results should be checked to make sure they are visible;
|
||||
* normally this should be equal to the shouldIncludeDeleted setting, but in cases where the
|
||||
* query could not check deletion status (due to Datastore limitations such as the limit of
|
||||
* one field queried for inequality, for instance), it may need to be set to true even when
|
||||
* not including deleted records
|
||||
* @param querySizeLimit the maximum number of items the query is expected to return, usually
|
||||
* because the limit has been set
|
||||
* @return an {@link RdapResultSet} object containing the list of resources and an incompleteness
|
||||
* warning flag, which is set to MIGHT_BE_INCOMPLETE iff any resources were excluded due to
|
||||
* lack of visibility, and the resulting list of resources is less than the maximum allowable,
|
||||
* and the number of items returned by the query is greater than or equal to the maximum
|
||||
* number we might have expected
|
||||
*/
|
||||
<T extends EppResource> RdapResultSet<T> getMatchingResourcesSql(
|
||||
CriteriaQueryBuilder<T> builder, boolean checkForVisibility, int querySizeLimit) {
|
||||
jpaTm().assertInTransaction();
|
||||
Optional<String> desiredRegistrar = getDesiredRegistrar();
|
||||
if (desiredRegistrar.isPresent()) {
|
||||
builder =
|
||||
builder.where(
|
||||
jpaTm().getEntityManager().getCriteriaBuilder()::equal,
|
||||
"currentSponsorClientId",
|
||||
desiredRegistrar.get());
|
||||
}
|
||||
List<T> queryResult =
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createQuery(builder.build())
|
||||
.setMaxResults(querySizeLimit)
|
||||
.getResultList();
|
||||
if (checkForVisibility) {
|
||||
return filterResourcesByVisibility(queryResult, querySizeLimit);
|
||||
} else {
|
||||
return RdapResultSet.create(queryResult);
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends EppResource> RdapResultSet<T> filterResourcesByVisibility(
|
||||
List<T> queryResult, int querySizeLimit) {
|
||||
// If we are including deleted resources, we need to check that we're authorized for each one.
|
||||
List<T> resources = new ArrayList<>();
|
||||
int numResourcesQueried = 0;
|
||||
boolean someExcluded = false;
|
||||
for (T resource : query) {
|
||||
for (T resource : queryResult) {
|
||||
if (shouldBeVisible(resource)) {
|
||||
resources.add(resource);
|
||||
} else {
|
||||
@@ -268,8 +321,8 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
|
||||
// to be (more) sure we got everything.
|
||||
int getStandardQuerySizeLimit() {
|
||||
return shouldIncludeDeleted()
|
||||
? (RESULT_SET_SIZE_SCALING_FACTOR * (rdapResultSetMaxSize + 1))
|
||||
: (rdapResultSetMaxSize + 1);
|
||||
? (RESULT_SET_SIZE_SCALING_FACTOR * (rdapResultSetMaxSize + 1))
|
||||
: (rdapResultSetMaxSize + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -311,9 +364,10 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
|
||||
query = query.filter(filterField, partialStringQuery.getInitialString());
|
||||
} else {
|
||||
// Ignore the suffix; the caller will need to filter on the suffix, if any.
|
||||
query = query
|
||||
.filter(filterField + " >=", partialStringQuery.getInitialString())
|
||||
.filter(filterField + " <", partialStringQuery.getNextInitialString());
|
||||
query =
|
||||
query
|
||||
.filter(filterField + " >=", partialStringQuery.getInitialString())
|
||||
.filter(filterField + " <", partialStringQuery.getNextInitialString());
|
||||
}
|
||||
if (cursorString.isPresent()) {
|
||||
query = query.filter(filterField + " >", cursorString.get());
|
||||
@@ -321,6 +375,59 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
|
||||
return setOtherQueryAttributes(query, deletedItemHandling, resultSetMaxSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* In Cloud SQL, handles prefix searches in cases where, if we need to filter out deleted items,
|
||||
* there are no pending deletes.
|
||||
*
|
||||
* <p>In such cases, it is sufficient to check whether {@code deletionTime} is equal to {@code
|
||||
* END_OF_TIME}, because any other value means it has already been deleted. This allows us to use
|
||||
* an equality query for the deletion time.
|
||||
*
|
||||
* @param clazz the type of resource to be queried
|
||||
* @param filterField the database field of interest
|
||||
* @param partialStringQuery the details of the search string; if there is no wildcard, an
|
||||
* equality query is used; if there is a wildcard, a range query is used instead; the initial
|
||||
* string should not be empty, and any search suffix will be ignored, so the caller must
|
||||
* filter the results if a suffix is specified
|
||||
* @param cursorString if a cursor is present, this parameter should specify the cursor string, to
|
||||
* skip any results up to and including the string; empty() if there is no cursor
|
||||
* @param deletedItemHandling whether to include or exclude deleted items
|
||||
* @return a {@link CriteriaQueryBuilder} object representing the query so far
|
||||
*/
|
||||
static <T extends EppResource> CriteriaQueryBuilder<T> queryItemsSql(
|
||||
Class<T> clazz,
|
||||
String filterField,
|
||||
RdapSearchPattern partialStringQuery,
|
||||
Optional<String> cursorString,
|
||||
DeletedItemHandling deletedItemHandling) {
|
||||
jpaTm().assertInTransaction();
|
||||
if (partialStringQuery.getInitialString().length()
|
||||
< RdapSearchPattern.MIN_INITIAL_STRING_LENGTH) {
|
||||
throw new UnprocessableEntityException(
|
||||
String.format(
|
||||
"Initial search string must be at least %d characters",
|
||||
RdapSearchPattern.MIN_INITIAL_STRING_LENGTH));
|
||||
}
|
||||
CriteriaBuilder criteriaBuilder = jpaTm().getEntityManager().getCriteriaBuilder();
|
||||
CriteriaQueryBuilder<T> builder = CriteriaQueryBuilder.create(clazz);
|
||||
if (partialStringQuery.getHasWildcard()) {
|
||||
builder =
|
||||
builder.where(
|
||||
criteriaBuilder::like,
|
||||
filterField,
|
||||
String.format("%s%%", partialStringQuery.getInitialString()));
|
||||
} else {
|
||||
// no wildcard means we use a standard equals query
|
||||
builder =
|
||||
builder.where(criteriaBuilder::equal, filterField, partialStringQuery.getInitialString());
|
||||
}
|
||||
if (cursorString.isPresent()) {
|
||||
builder = builder.where(criteriaBuilder::greaterThan, filterField, cursorString.get());
|
||||
}
|
||||
builder = builder.orderBy(criteriaBuilder::asc, filterField);
|
||||
return setDeletedItemHandlingSql(builder, deletedItemHandling);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles searches using a simple string rather than an {@link RdapSearchPattern}.
|
||||
*
|
||||
@@ -331,9 +438,9 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
|
||||
* @param filterField the database field of interest
|
||||
* @param queryString the search string
|
||||
* @param cursorField the field which should be compared to the cursor string, or empty() if the
|
||||
* key should be compared to a key created from the cursor string
|
||||
* key should be compared to a key created from the cursor string
|
||||
* @param cursorString if a cursor is present, this parameter should specify the cursor string, to
|
||||
* skip any results up to and including the string; empty() if there is no cursor
|
||||
* skip any results up to and including the string; empty() if there is no cursor
|
||||
* @param deletedItemHandling whether to include or exclude deleted items
|
||||
* @param resultSetMaxSize the maximum number of results to return
|
||||
* @return the query object
|
||||
@@ -363,6 +470,50 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
|
||||
return setOtherQueryAttributes(query, deletedItemHandling, resultSetMaxSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* In Cloud SQL, handles searches using a simple string rather than an {@link RdapSearchPattern}.
|
||||
*
|
||||
* <p>Since the filter is not an inequality, we can support also checking a cursor string against
|
||||
* a different field (which involves an inequality on that field).
|
||||
*
|
||||
* @param clazz the type of resource to be queried
|
||||
* @param filterField the database field of interest
|
||||
* @param queryString the search string
|
||||
* @param cursorField the field which should be compared to the cursor string, or empty() if the
|
||||
* key should be compared to a key created from the cursor string
|
||||
* @param cursorString if a cursor is present, this parameter should specify the cursor string, to
|
||||
* skip any results up to and including the string; empty() if there is no cursor
|
||||
* @param deletedItemHandling whether to include or exclude deleted items
|
||||
* @return a {@link CriteriaQueryBuilder} object representing the query so far
|
||||
*/
|
||||
static <T extends EppResource> CriteriaQueryBuilder<T> queryItemsSql(
|
||||
Class<T> clazz,
|
||||
String filterField,
|
||||
String queryString,
|
||||
Optional<String> cursorField,
|
||||
Optional<String> cursorString,
|
||||
DeletedItemHandling deletedItemHandling) {
|
||||
if (queryString.length() < RdapSearchPattern.MIN_INITIAL_STRING_LENGTH) {
|
||||
throw new UnprocessableEntityException(
|
||||
String.format(
|
||||
"Initial search string must be at least %d characters",
|
||||
RdapSearchPattern.MIN_INITIAL_STRING_LENGTH));
|
||||
}
|
||||
jpaTm().assertInTransaction();
|
||||
CriteriaQueryBuilder<T> builder = CriteriaQueryBuilder.create(clazz);
|
||||
CriteriaBuilder criteriaBuilder = jpaTm().getEntityManager().getCriteriaBuilder();
|
||||
builder = builder.where(criteriaBuilder::equal, filterField, queryString);
|
||||
if (cursorString.isPresent()) {
|
||||
if (cursorField.isPresent()) {
|
||||
builder =
|
||||
builder.where(criteriaBuilder::greaterThan, cursorField.get(), cursorString.get());
|
||||
} else {
|
||||
builder = builder.where(criteriaBuilder::greaterThan, "repoId", cursorString.get());
|
||||
}
|
||||
}
|
||||
return setDeletedItemHandlingSql(builder, deletedItemHandling);
|
||||
}
|
||||
|
||||
/** Handles searches where the field to be searched is the key. */
|
||||
static <T extends EppResource> Query<T> queryItemsByKey(
|
||||
Class<T> clazz,
|
||||
@@ -382,9 +533,10 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
|
||||
query = query.filterKey("=", Key.create(clazz, partialStringQuery.getInitialString()));
|
||||
} else {
|
||||
// Ignore the suffix; the caller will need to filter on the suffix, if any.
|
||||
query = query
|
||||
.filterKey(">=", Key.create(clazz, partialStringQuery.getInitialString()))
|
||||
.filterKey("<", Key.create(clazz, partialStringQuery.getNextInitialString()));
|
||||
query =
|
||||
query
|
||||
.filterKey(">=", Key.create(clazz, partialStringQuery.getInitialString()))
|
||||
.filterKey("<", Key.create(clazz, partialStringQuery.getNextInitialString()));
|
||||
}
|
||||
if (cursorString.isPresent()) {
|
||||
query = query.filterKey(">", Key.create(clazz, cursorString.get()));
|
||||
@@ -392,6 +544,26 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
|
||||
return setOtherQueryAttributes(query, deletedItemHandling, resultSetMaxSize);
|
||||
}
|
||||
|
||||
/** In Cloud SQL, handles searches where the field to be searched is the key. */
|
||||
static <T extends EppResource> CriteriaQueryBuilder<T> queryItemsByKeySql(
|
||||
Class<T> clazz,
|
||||
RdapSearchPattern partialStringQuery,
|
||||
Optional<String> cursorString,
|
||||
DeletedItemHandling deletedItemHandling) {
|
||||
jpaTm().assertInTransaction();
|
||||
return queryItemsSql(clazz, "repoId", partialStringQuery, cursorString, deletedItemHandling);
|
||||
}
|
||||
|
||||
static <T extends EppResource> CriteriaQueryBuilder<T> setDeletedItemHandlingSql(
|
||||
CriteriaQueryBuilder<T> builder, DeletedItemHandling deletedItemHandling) {
|
||||
if (!Objects.equals(deletedItemHandling, DeletedItemHandling.INCLUDE)) {
|
||||
builder =
|
||||
builder.where(
|
||||
jpaTm().getEntityManager().getCriteriaBuilder()::equal, "deletionTime", END_OF_TIME);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
static <T extends EppResource> Query<T> setOtherQueryAttributes(
|
||||
Query<T> query, DeletedItemHandling deletedItemHandling, int resultSetMaxSize) {
|
||||
if (deletedItemHandling != DeletedItemHandling.INCLUDE) {
|
||||
|
||||
@@ -135,7 +135,7 @@ public class RequestHandler<C> {
|
||||
return;
|
||||
}
|
||||
if (!route.get().isMethodAllowed(method)) {
|
||||
logger.atInfo().log("Method %s not allowed for: %s", method, path);
|
||||
logger.atWarning().log("Method %s not allowed for: %s", method, path);
|
||||
rsp.sendError(SC_METHOD_NOT_ALLOWED);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.schema.replay;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
import google.registry.model.ImmutableObject;
|
||||
|
||||
/** Datastore entity to keep track of the last SQL transaction imported into the datastore. */
|
||||
@Entity
|
||||
public class LastSqlTransaction extends ImmutableObject implements DatastoreOnlyEntity {
|
||||
|
||||
/** The key for this singleton. */
|
||||
public static final Key<LastSqlTransaction> KEY = Key.create(LastSqlTransaction.class, 1);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Id
|
||||
private long id = 1;
|
||||
|
||||
private long transactionId;
|
||||
|
||||
LastSqlTransaction() {}
|
||||
|
||||
@VisibleForTesting
|
||||
LastSqlTransaction(long newTransactionId) {
|
||||
transactionId = newTransactionId;
|
||||
}
|
||||
|
||||
LastSqlTransaction cloneWithNewTransactionId(long transactionId) {
|
||||
checkArgument(
|
||||
transactionId > this.transactionId,
|
||||
"New transaction id (%s) must be greater than the current transaction id (%s)",
|
||||
transactionId,
|
||||
this.transactionId);
|
||||
return new LastSqlTransaction(transactionId);
|
||||
}
|
||||
|
||||
long getTransactionId() {
|
||||
return transactionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the instance.
|
||||
*
|
||||
* <p>Must be called within an Ofy transaction.
|
||||
*
|
||||
* <p>Creates a new instance of the singleton if it is not already present in Cloud Datastore,
|
||||
*/
|
||||
static LastSqlTransaction load() {
|
||||
ofy().assertInTransaction();
|
||||
LastSqlTransaction result = ofy().load().key(KEY).now();
|
||||
return result == null ? new LastSqlTransaction() : result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.schema.replay;
|
||||
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.persistence.transaction.Transaction;
|
||||
import google.registry.persistence.transaction.TransactionEntity;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.auth.Auth;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import javax.persistence.NoResultException;
|
||||
|
||||
/** Cron task to replicate from Cloud SQL to datastore. */
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
path = ReplicateToDatastoreAction.PATH,
|
||||
method = GET,
|
||||
automaticallyPrintOk = true,
|
||||
auth = Auth.AUTH_INTERNAL_OR_ADMIN)
|
||||
class ReplicateToDatastoreAction implements Runnable {
|
||||
public static final String PATH = "/_dr/cron/replicateToDatastore";
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
/**
|
||||
* Number of transactions to fetch from SQL. The rationale for 200 is that we're processing these
|
||||
* every minute and our production instance currently does about 2 mutations per second, so this
|
||||
* should generally be enough to scoop up all of the transactions for the past minute.
|
||||
*/
|
||||
public static final int BATCH_SIZE = 200;
|
||||
|
||||
@VisibleForTesting
|
||||
List<TransactionEntity> getTransactionBatch() {
|
||||
// Get the next batch of transactions that we haven't replicated.
|
||||
LastSqlTransaction lastSqlTxnBeforeBatch = ofyTm().transact(() -> LastSqlTransaction.load());
|
||||
try {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createQuery(
|
||||
"SELECT txn FROM TransactionEntity txn WHERE id >"
|
||||
+ " :lastId ORDER BY id")
|
||||
.setParameter("lastId", lastSqlTxnBeforeBatch.getTransactionId())
|
||||
.setMaxResults(BATCH_SIZE)
|
||||
.getResultList());
|
||||
} catch (NoResultException e) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a transaction to datastore, returns true if there was a fatal error and the batch should
|
||||
* be aborted.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean applyTransaction(TransactionEntity txnEntity) {
|
||||
logger.atInfo().log("Applying a single transaction Cloud SQL -> Cloud Datastore");
|
||||
return ofyTm()
|
||||
.transact(
|
||||
() -> {
|
||||
// Reload the last transaction id, which could possibly have changed.
|
||||
LastSqlTransaction lastSqlTxn = LastSqlTransaction.load();
|
||||
long nextTxnId = lastSqlTxn.getTransactionId() + 1;
|
||||
if (nextTxnId < txnEntity.getId()) {
|
||||
// We're missing a transaction. This is bad. Transaction ids are supposed to
|
||||
// increase monotonically, so we abort rather than applying anything out of
|
||||
// order.
|
||||
logger.atSevere().log(
|
||||
"Missing transaction: last transaction id = %s, next available transaction "
|
||||
+ "= %s",
|
||||
nextTxnId - 1, txnEntity.getId());
|
||||
return true;
|
||||
} else if (nextTxnId > txnEntity.getId()) {
|
||||
// We've already replayed this transaction. This shouldn't happen, as GAE cron
|
||||
// is supposed to avoid overruns and this action shouldn't be executed from any
|
||||
// other context, but it's not harmful as we can just ignore the transaction. Log
|
||||
// it so that we know about it and move on.
|
||||
logger.atWarning().log(
|
||||
"Ignoring transaction %s, which appears to have already been applied.",
|
||||
txnEntity.getId());
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.atInfo().log("Applying transaction %s to Cloud Datastore", txnEntity.getId());
|
||||
|
||||
// At this point, we know txnEntity is the correct next transaction, so write it
|
||||
// to datastore.
|
||||
try {
|
||||
Transaction.deserialize(txnEntity.getContents()).writeToDatastore();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error during transaction deserialization.", e);
|
||||
}
|
||||
|
||||
// Write the updated last transaction id to datastore as part of this datastore
|
||||
// transaction.
|
||||
ofy().save().entity(lastSqlTxn.cloneWithNewTransactionId(nextTxnId));
|
||||
logger.atInfo().log(
|
||||
"Finished applying single transaction Cloud SQL -> Cloud Datastore");
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// TODO(b/181758163): Deal with objects that don't exist in Cloud SQL, e.g. ForeignKeyIndex,
|
||||
// EppResourceIndex.
|
||||
logger.atInfo().log("Processing transaction replay batch Cloud SQL -> Cloud Datastore");
|
||||
for (TransactionEntity txnEntity : getTransactionBatch()) {
|
||||
if (applyTransaction(txnEntity)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
logger.atInfo().log("Done processing transaction replay batch Cloud SQL -> Cloud Datastore");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.Streams;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** Retrieves and prints one or more premium lists. */
|
||||
@Parameters(separators = " =", commandDescription = "Show one or more premium lists")
|
||||
public class GetPremiumListCommand implements CommandWithRemoteApi {
|
||||
|
||||
@Parameter(description = "Name(s) of the premium list(s) to retrieve", required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
for (String premiumListName : mainParameters) {
|
||||
if (PremiumListDualDao.exists(premiumListName)) {
|
||||
System.out.printf(
|
||||
"%s:\n%s\n",
|
||||
premiumListName,
|
||||
Streams.stream(PremiumListDualDao.loadAllPremiumListEntries(premiumListName))
|
||||
.sorted(Comparator.comparing(PremiumListEntry::getLabel))
|
||||
.map(PremiumListEntry::toString)
|
||||
.collect(Collectors.joining("\n")));
|
||||
} else {
|
||||
System.out.println(String.format("No list found with name %s.", premiumListName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,6 +76,7 @@ public final class RegistryTool {
|
||||
.put("get_host", GetHostCommand.class)
|
||||
.put("get_keyring_secret", GetKeyringSecretCommand.class)
|
||||
.put("get_operation_status", GetOperationStatusCommand.class)
|
||||
.put("get_premium_list", GetPremiumListCommand.class)
|
||||
.put("get_registrar", GetRegistrarCommand.class)
|
||||
.put("get_resource_by_key", GetResourceByKeyCommand.class)
|
||||
.put("get_routing_map", GetRoutingMapCommand.class)
|
||||
|
||||
@@ -43,7 +43,7 @@ import google.registry.request.Modules.UrlFetchTransportModule;
|
||||
import google.registry.request.Modules.UserServiceModule;
|
||||
import google.registry.tools.AuthModule.LocalCredentialModule;
|
||||
import google.registry.util.UtilsModule;
|
||||
import google.registry.whois.WhoisModule;
|
||||
import google.registry.whois.NonCachingWhoisModule;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@@ -81,7 +81,7 @@ import javax.inject.Singleton;
|
||||
UserServiceModule.class,
|
||||
UtilsModule.class,
|
||||
VoidDnsWriterModule.class,
|
||||
WhoisModule.class
|
||||
NonCachingWhoisModule.class
|
||||
})
|
||||
interface RegistryToolComponent {
|
||||
void inject(AckPollMessagesCommand command);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.whois;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.whois.WhoisMetrics.WhoisMetric;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Dagger base module for the whois package.
|
||||
*
|
||||
* <p>Provides whois objects common to both the normal ({@link WhoisModule}) and non-caching ({@link
|
||||
* NonCachingWhoisModule}) implementations.
|
||||
*/
|
||||
@Module
|
||||
class BaseWhoisModule {
|
||||
|
||||
@Provides
|
||||
@SuppressWarnings("CloseableProvides")
|
||||
static Reader provideHttpInputReader(HttpServletRequest req) {
|
||||
try {
|
||||
return req.getReader();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a {@link WhoisMetrics.WhoisMetric.Builder} with the startTimestamp already
|
||||
* initialized.
|
||||
*/
|
||||
@Provides
|
||||
static WhoisMetric.Builder provideEppMetricBuilder(Clock clock) {
|
||||
return WhoisMetric.builderForRequest(clock);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.whois;
|
||||
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyCached;
|
||||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
@@ -25,20 +26,27 @@ import org.joda.time.DateTime;
|
||||
public class DomainLookupCommand extends DomainOrHostLookupCommand {
|
||||
|
||||
private final boolean fullOutput;
|
||||
private final boolean cached;
|
||||
private final String whoisRedactedEmailText;
|
||||
|
||||
public DomainLookupCommand(
|
||||
InternetDomainName domainName,
|
||||
boolean fullOutput,
|
||||
boolean cached,
|
||||
String whoisRedactedEmailText) {
|
||||
super(domainName, "Domain");
|
||||
this.fullOutput = fullOutput;
|
||||
this.cached = cached;
|
||||
this.whoisRedactedEmailText = whoisRedactedEmailText;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<WhoisResponse> getResponse(InternetDomainName domainName, DateTime now) {
|
||||
return loadByForeignKeyCached(DomainBase.class, domainName.toString(), now)
|
||||
.map(domain -> new DomainWhoisResponse(domain, fullOutput, whoisRedactedEmailText, now));
|
||||
Optional<DomainBase> domainResource =
|
||||
cached
|
||||
? loadByForeignKeyCached(DomainBase.class, domainName.toString(), now)
|
||||
: loadByForeignKey(DomainBase.class, domainName.toString(), now);
|
||||
return domainResource.map(
|
||||
domain -> new DomainWhoisResponse(domain, fullOutput, whoisRedactedEmailText, now));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.whois;
|
||||
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyCached;
|
||||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
@@ -24,13 +25,19 @@ import org.joda.time.DateTime;
|
||||
/** Represents a WHOIS lookup on a nameserver based on its hostname. */
|
||||
public class NameserverLookupByHostCommand extends DomainOrHostLookupCommand {
|
||||
|
||||
NameserverLookupByHostCommand(InternetDomainName hostName) {
|
||||
boolean cached;
|
||||
|
||||
NameserverLookupByHostCommand(InternetDomainName hostName, boolean cached) {
|
||||
super(hostName, "Nameserver");
|
||||
this.cached = cached;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<WhoisResponse> getResponse(InternetDomainName hostName, DateTime now) {
|
||||
return loadByForeignKeyCached(HostResource.class, hostName.toString(), now)
|
||||
.map(host -> new NameserverWhoisResponse(host, now));
|
||||
Optional<HostResource> hostResource =
|
||||
cached
|
||||
? loadByForeignKeyCached(HostResource.class, hostName.toString(), now)
|
||||
: loadByForeignKey(HostResource.class, hostName.toString(), now);
|
||||
return hostResource.map(host -> new NameserverWhoisResponse(host, now));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.whois;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
|
||||
/**
|
||||
* Whois module for systems that require that we not cache EPP resources (e.g. the nomulus tool).
|
||||
*
|
||||
* <h3>Dependencies</h3>
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link google.registry.request.RequestModule RequestModule}
|
||||
* </ul>
|
||||
*
|
||||
* @see "google.registry.tools.RegistryToolComponent"
|
||||
*/
|
||||
@Module
|
||||
public final class NonCachingWhoisModule extends BaseWhoisModule {
|
||||
@Provides
|
||||
@Config("whoisCommandFactory")
|
||||
static WhoisCommandFactory provideWhoisCommandFactory() {
|
||||
return WhoisCommandFactory.createNonCached();
|
||||
}
|
||||
}
|
||||
@@ -39,61 +39,66 @@ final class RegistrarLookupCommand implements WhoisCommand {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
/** True if the command should return cached responses. */
|
||||
private boolean cached;
|
||||
|
||||
/**
|
||||
* Cache of a map from a stripped-down (letters and digits only) name to the registrar. This map
|
||||
* includes only active, publicly visible registrars, because the others should be invisible to
|
||||
* WHOIS.
|
||||
*/
|
||||
private static final Supplier<Map<String, Registrar>> REGISTRAR_BY_NORMALIZED_NAME_CACHE =
|
||||
memoizeWithShortExpiration(
|
||||
() -> {
|
||||
Map<String, Registrar> map = new HashMap<>();
|
||||
// Use the normalized registrar name as a key, and ignore inactive and hidden
|
||||
// registrars.
|
||||
for (Registrar registrar : Registrar.loadAllCached()) {
|
||||
if (!registrar.isLiveAndPubliclyVisible() || registrar.getRegistrarName() == null) {
|
||||
continue;
|
||||
}
|
||||
String normalized = normalizeRegistrarName(registrar.getRegistrarName());
|
||||
if (map.put(normalized, registrar) != null) {
|
||||
logger.atWarning().log(
|
||||
"%s appeared as a normalized registrar name for more than one registrar.",
|
||||
normalized);
|
||||
}
|
||||
}
|
||||
// Use the normalized registrar name without its last word as a key, assuming there are
|
||||
// multiple words in the name. This allows searches without LLC or INC, etc. Only insert
|
||||
// if there isn't already a mapping for this string, so that if there's a registrar with
|
||||
// a
|
||||
// two word name (Go Daddy) and no business-type suffix and another registrar with just
|
||||
// that first word as its name (Go), the latter will win.
|
||||
for (Registrar registrar : ImmutableList.copyOf(map.values())) {
|
||||
if (registrar.getRegistrarName() == null) {
|
||||
continue;
|
||||
}
|
||||
List<String> words =
|
||||
Splitter.on(CharMatcher.whitespace()).splitToList(registrar.getRegistrarName());
|
||||
if (words.size() > 1) {
|
||||
String normalized =
|
||||
normalizeRegistrarName(Joiner.on("").join(words.subList(0, words.size() - 1)));
|
||||
map.putIfAbsent(normalized, registrar);
|
||||
}
|
||||
}
|
||||
return ImmutableMap.copyOf(map);
|
||||
});
|
||||
memoizeWithShortExpiration(RegistrarLookupCommand::loadRegistrarMap);
|
||||
|
||||
@VisibleForTesting
|
||||
final String registrarName;
|
||||
|
||||
RegistrarLookupCommand(String registrarName) {
|
||||
RegistrarLookupCommand(String registrarName, boolean cached) {
|
||||
checkArgument(!isNullOrEmpty(registrarName), "registrarName");
|
||||
this.registrarName = registrarName;
|
||||
this.cached = cached;
|
||||
}
|
||||
|
||||
static Map<String, Registrar> loadRegistrarMap() {
|
||||
Map<String, Registrar> map = new HashMap<>();
|
||||
// Use the normalized registrar name as a key, and ignore inactive and hidden
|
||||
// registrars.
|
||||
for (Registrar registrar : Registrar.loadAll()) {
|
||||
if (!registrar.isLiveAndPubliclyVisible() || registrar.getRegistrarName() == null) {
|
||||
continue;
|
||||
}
|
||||
String normalized = normalizeRegistrarName(registrar.getRegistrarName());
|
||||
if (map.put(normalized, registrar) != null) {
|
||||
logger.atWarning().log(
|
||||
"%s appeared as a normalized registrar name for more than one registrar.", normalized);
|
||||
}
|
||||
}
|
||||
// Use the normalized registrar name without its last word as a key, assuming there are
|
||||
// multiple words in the name. This allows searches without LLC or INC, etc. Only insert
|
||||
// if there isn't already a mapping for this string, so that if there's a registrar with
|
||||
// a
|
||||
// two word name (Go Daddy) and no business-type suffix and another registrar with just
|
||||
// that first word as its name (Go), the latter will win.
|
||||
for (Registrar registrar : ImmutableList.copyOf(map.values())) {
|
||||
if (registrar.getRegistrarName() == null) {
|
||||
continue;
|
||||
}
|
||||
List<String> words =
|
||||
Splitter.on(CharMatcher.whitespace()).splitToList(registrar.getRegistrarName());
|
||||
if (words.size() > 1) {
|
||||
String normalized =
|
||||
normalizeRegistrarName(Joiner.on("").join(words.subList(0, words.size() - 1)));
|
||||
map.putIfAbsent(normalized, registrar);
|
||||
}
|
||||
}
|
||||
return ImmutableMap.copyOf(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WhoisResponse executeQuery(DateTime now) throws WhoisException {
|
||||
Registrar registrar =
|
||||
REGISTRAR_BY_NORMALIZED_NAME_CACHE.get().get(normalizeRegistrarName(registrarName));
|
||||
Map<String, Registrar> registrars =
|
||||
cached ? REGISTRAR_BY_NORMALIZED_NAME_CACHE.get() : loadRegistrarMap();
|
||||
Registrar registrar = registrars.get(normalizeRegistrarName(registrarName));
|
||||
// If a registrar is in the cache, we know it must be active and publicly visible.
|
||||
if (registrar == null) {
|
||||
throw new WhoisException(now, SC_NOT_FOUND, "No registrar found.");
|
||||
|
||||
@@ -26,10 +26,30 @@ import java.net.InetAddress;
|
||||
*/
|
||||
public class WhoisCommandFactory {
|
||||
|
||||
/** True if the commands should return cached responses. */
|
||||
private boolean cached = true;
|
||||
|
||||
/** Public default constructor, needed so we can construct this from the class name. */
|
||||
public WhoisCommandFactory() {}
|
||||
|
||||
private WhoisCommandFactory(boolean cached) {
|
||||
this.cached = cached;
|
||||
}
|
||||
|
||||
/** Create a command factory that does not rely on entity caches. */
|
||||
static WhoisCommandFactory createNonCached() {
|
||||
return new WhoisCommandFactory(false);
|
||||
}
|
||||
|
||||
/** Create a command factory that may rely on entity caches. */
|
||||
static WhoisCommandFactory createCached() {
|
||||
return new WhoisCommandFactory(true);
|
||||
}
|
||||
|
||||
/** Returns a new {@link WhoisCommand} to perform a domain lookup on the specified domain name. */
|
||||
public WhoisCommand domainLookup(
|
||||
InternetDomainName domainName, boolean fullOutput, String whoisRedactedEmailText) {
|
||||
return new DomainLookupCommand(domainName, fullOutput, whoisRedactedEmailText);
|
||||
return new DomainLookupCommand(domainName, fullOutput, cached, whoisRedactedEmailText);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,7 +63,7 @@ public class WhoisCommandFactory {
|
||||
* Returns a new {@link WhoisCommand} to perform a nameserver lookup on the specified host name.
|
||||
*/
|
||||
public WhoisCommand nameserverLookupByHost(InternetDomainName hostName) {
|
||||
return new NameserverLookupByHostCommand(hostName);
|
||||
return new NameserverLookupByHostCommand(hostName, cached);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,6 +71,6 @@ public class WhoisCommandFactory {
|
||||
* name.
|
||||
*/
|
||||
public WhoisCommand registrarLookup(String registrar) {
|
||||
return new RegistrarLookupCommand(registrar);
|
||||
return new RegistrarLookupCommand(registrar, cached);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,6 @@ import static google.registry.util.TypeUtils.instantiate;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.whois.WhoisMetrics.WhoisMetric;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Dagger module for the whois package.
|
||||
@@ -38,31 +33,11 @@ import javax.servlet.http.HttpServletRequest;
|
||||
* @see "google.registry.module.frontend.FrontendComponent"
|
||||
*/
|
||||
@Module
|
||||
public final class WhoisModule {
|
||||
|
||||
@Provides
|
||||
@SuppressWarnings("CloseableProvides")
|
||||
static Reader provideHttpInputReader(HttpServletRequest req) {
|
||||
try {
|
||||
return req.getReader();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public final class WhoisModule extends BaseWhoisModule {
|
||||
@Provides
|
||||
@Config("whoisCommandFactory")
|
||||
static WhoisCommandFactory provideWhoisCommandFactory(
|
||||
@Config("whoisCommandFactoryClass") String factoryClass) {
|
||||
return instantiate(getClassFromString(factoryClass, WhoisCommandFactory.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a {@link WhoisMetrics.WhoisMetric.Builder} with the startTimestamp already
|
||||
* initialized.
|
||||
*/
|
||||
@Provides
|
||||
static WhoisMetric.Builder provideEppMetricBuilder(Clock clock) {
|
||||
return WhoisMetric.builderForRequest(clock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ class WhoisReader {
|
||||
if (!tld.isPresent()) {
|
||||
// This target is not under any configured TLD, so just try it as a registrar name.
|
||||
logger.atInfo().log("Attempting registrar lookup using %s as a registrar", arg1);
|
||||
return new RegistrarLookupCommand(arg1);
|
||||
return commandFactory.registrarLookup(arg1);
|
||||
}
|
||||
|
||||
// If the target is exactly one level above the TLD, then this is a second level domain
|
||||
|
||||
@@ -25,12 +25,19 @@ import google.registry.flows.ResourceCheckFlowTestCase;
|
||||
import google.registry.flows.exceptions.TooManyResourceChecksException;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.ReplayExtension;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link ContactCheckFlow}. */
|
||||
@DualDatabaseTest
|
||||
class ContactCheckFlowTest extends ResourceCheckFlowTestCase<ContactCheckFlow, ContactResource> {
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
|
||||
ContactCheckFlowTest() {
|
||||
setEppInput("contact_check.xml");
|
||||
}
|
||||
|
||||
@@ -33,13 +33,20 @@ import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientExcept
|
||||
import google.registry.flows.exceptions.ResourceCreateContentionException;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.ReplayExtension;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link ContactCreateFlow}. */
|
||||
@DualDatabaseTest
|
||||
class ContactCreateFlowTest extends ResourceFlowTestCase<ContactCreateFlow, ContactResource> {
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
|
||||
ContactCreateFlowTest() {
|
||||
setEppInput("contact_create.xml");
|
||||
clock.setTo(DateTime.parse("1999-04-03T22:00:00.0Z"));
|
||||
|
||||
@@ -38,13 +38,20 @@ import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.ReplayExtension;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link ContactDeleteFlow}. */
|
||||
@DualDatabaseTest
|
||||
class ContactDeleteFlowTest extends ResourceFlowTestCase<ContactDeleteFlow, ContactResource> {
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
|
||||
@BeforeEach
|
||||
void initFlowTest() {
|
||||
setEppInput("contact_delete.xml");
|
||||
|
||||
@@ -39,13 +39,20 @@ import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
|
||||
import google.registry.model.eppcommon.PresenceMarker;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.ReplayExtension;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link ContactInfoFlow}. */
|
||||
@DualDatabaseTest
|
||||
class ContactInfoFlowTest extends ResourceFlowTestCase<ContactInfoFlow, ContactResource> {
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
|
||||
ContactInfoFlowTest() {
|
||||
setEppInput("contact_info.xml");
|
||||
}
|
||||
|
||||
@@ -38,14 +38,21 @@ import google.registry.model.transfer.TransferData;
|
||||
import google.registry.model.transfer.TransferResponse;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.ReplayExtension;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link ContactTransferCancelFlow}. */
|
||||
@DualDatabaseTest
|
||||
class ContactTransferCancelFlowTest
|
||||
extends ContactTransferFlowTestCase<ContactTransferCancelFlow, ContactResource> {
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.setEppInput("contact_transfer_cancel.xml");
|
||||
|
||||
@@ -40,14 +40,21 @@ import google.registry.model.transfer.TransferData;
|
||||
import google.registry.model.transfer.TransferResponse;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.ReplayExtension;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link ContactTransferRejectFlow}. */
|
||||
@DualDatabaseTest
|
||||
class ContactTransferRejectFlowTest
|
||||
extends ContactTransferFlowTestCase<ContactTransferRejectFlow, ContactResource> {
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
setEppInput("contact_transfer_reject.xml");
|
||||
|
||||
@@ -52,15 +52,22 @@ import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.transfer.ContactTransferData;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.ReplayExtension;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link ContactTransferRequestFlow}. */
|
||||
@DualDatabaseTest
|
||||
class ContactTransferRequestFlowTest
|
||||
extends ContactTransferFlowTestCase<ContactTransferRequestFlow, ContactResource> {
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
|
||||
ContactTransferRequestFlowTest() {
|
||||
// We need the transfer to happen at exactly this time in order for the response to match up.
|
||||
clock.setTo(DateTime.parse("2000-06-08T22:00:00.0Z"));
|
||||
|
||||
@@ -71,16 +71,29 @@ import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.Registry.TldState;
|
||||
import google.registry.model.registry.label.ReservedList;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.testing.ReplayExtension;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link DomainCheckFlow}. */
|
||||
class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, DomainBase> {
|
||||
|
||||
@Order(value = Order.DEFAULT - 3)
|
||||
@RegisterExtension
|
||||
final SetClockExtension setClockExtension = new SetClockExtension();
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
|
||||
DomainCheckFlowTest() {
|
||||
setEppInput("domain_check_one_tld.xml");
|
||||
}
|
||||
@@ -1338,4 +1351,12 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
|
||||
.build());
|
||||
}
|
||||
|
||||
class SetClockExtension implements BeforeEachCallback {
|
||||
@Override
|
||||
public void beforeEach(ExtensionContext context) {
|
||||
// Kick the clock back to before the earliest date that we set the clock to.
|
||||
clock.setTo(DateTime.parse("2009-01-01T10:00:00Z"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,13 +36,20 @@ import google.registry.flows.exceptions.TooManyResourceChecksException;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.Registry.TldState;
|
||||
import google.registry.testing.ReplayExtension;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link DomainClaimsCheckFlow}. */
|
||||
public class DomainClaimsCheckFlowTest
|
||||
extends ResourceFlowTestCase<DomainClaimsCheckFlow, DomainBase> {
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
|
||||
DomainClaimsCheckFlowTest() {
|
||||
setEppInput("domain_check_claims.xml");
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
|
||||
import static google.registry.testing.TestDataHelper.updateSubstitutions;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@@ -45,7 +46,8 @@ import google.registry.flows.domain.DomainFlowUtils.FeeChecksDontSupportPhasesEx
|
||||
import google.registry.flows.domain.DomainFlowUtils.RestoresAreAlwaysForOneYearException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.TransfersAreAlwaysForOneYearException;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.contact.ContactAuthInfo;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
@@ -63,13 +65,26 @@ import google.registry.model.registry.Registry;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.ReplayExtension;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link DomainInfoFlow}. */
|
||||
class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase> {
|
||||
|
||||
@Order(value = Order.DEFAULT - 3)
|
||||
@RegisterExtension
|
||||
final SetClockExtension setClockExtension = new SetClockExtension();
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
|
||||
/**
|
||||
* The domain_info_fee.xml default substitutions common to most tests.
|
||||
*
|
||||
@@ -93,7 +108,6 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
|
||||
void setup() {
|
||||
setEppInput("domain_info.xml");
|
||||
sessionMetadata.setClientId("NewRegistrar");
|
||||
clock.setTo(DateTime.parse("2005-03-03T22:00:00.000Z"));
|
||||
createTld("tld");
|
||||
persistResource(AppEngineExtension.makeRegistrar1().asBuilder().setClientId("ClientZ").build());
|
||||
}
|
||||
@@ -149,7 +163,10 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
|
||||
}
|
||||
|
||||
private void doSuccessfulTest(
|
||||
String expectedXmlFilename, boolean inactive, ImmutableMap<String, String> substitutions)
|
||||
String expectedXmlFilename,
|
||||
boolean inactive,
|
||||
ImmutableMap<String, String> substitutions,
|
||||
boolean expectHistoryAndBilling)
|
||||
throws Exception {
|
||||
assertTransactionalFlow(false);
|
||||
String expected =
|
||||
@@ -158,12 +175,20 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
|
||||
expected = expected.replaceAll("\"ok\"", "\"inactive\"");
|
||||
}
|
||||
runFlowAssertResponse(expected);
|
||||
assertNoHistory();
|
||||
assertNoBillingEvents();
|
||||
if (!expectHistoryAndBilling) {
|
||||
assertNoHistory();
|
||||
assertNoBillingEvents();
|
||||
}
|
||||
}
|
||||
|
||||
private void doSuccessfulTest(
|
||||
String expectedXmlFilename, boolean inactive, ImmutableMap<String, String> substitutions)
|
||||
throws Exception {
|
||||
doSuccessfulTest(expectedXmlFilename, inactive, substitutions, false);
|
||||
}
|
||||
|
||||
private void doSuccessfulTest(String expectedXmlFilename, boolean inactive) throws Exception {
|
||||
doSuccessfulTest(expectedXmlFilename, inactive, ImmutableMap.of());
|
||||
doSuccessfulTest(expectedXmlFilename, inactive, ImmutableMap.of(), false);
|
||||
}
|
||||
|
||||
private void doSuccessfulTest(String expectedXmlFilename) throws Exception {
|
||||
@@ -309,7 +334,11 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
|
||||
.asBuilder()
|
||||
.addGracePeriod(
|
||||
GracePeriod.create(
|
||||
gracePeriodStatus, domain.getRepoId(), clock.nowUtc().plusDays(1), "foo", null))
|
||||
gracePeriodStatus,
|
||||
domain.getRepoId(),
|
||||
clock.nowUtc().plusDays(1),
|
||||
"TheRegistrar",
|
||||
null))
|
||||
.setCreationClientId("NewRegistrar")
|
||||
.setCreationTimeForTest(DateTime.parse("2003-11-26T22:00:00.0Z"))
|
||||
.setRegistrationExpirationTime(DateTime.parse("2005-11-26T22:00:00.0Z"))
|
||||
@@ -328,10 +357,25 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
|
||||
@Test
|
||||
void testSuccess_autoRenewGracePeriod() throws Exception {
|
||||
persistTestEntities(false);
|
||||
Key<HistoryEntry> historyEntry =
|
||||
Key.create(domain.createVKey().getOfyKey(), HistoryEntry.class, 67890);
|
||||
VKey<BillingEvent.Recurring> recurringVKey =
|
||||
VKey.from(Key.create(historyEntry, Recurring.class, 12345));
|
||||
HistoryEntry historyEntry =
|
||||
persistResource(
|
||||
new HistoryEntry.Builder()
|
||||
.setParent(domain)
|
||||
.setType(HistoryEntry.Type.DOMAIN_CREATE)
|
||||
.setModificationTime(clock.nowUtc())
|
||||
.build());
|
||||
BillingEvent.Recurring renewEvent =
|
||||
persistResource(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setReason(Reason.RENEW)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setTargetId(getUniqueIdFromCommand())
|
||||
.setClientId("TheRegistrar")
|
||||
.setEventTime(clock.nowUtc())
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.setParent(historyEntry)
|
||||
.build());
|
||||
VKey<BillingEvent.Recurring> recurringVKey = renewEvent.createVKey();
|
||||
// Add an AUTO_RENEW grace period to the saved resource.
|
||||
persistResource(
|
||||
domain
|
||||
@@ -341,10 +385,10 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
|
||||
GracePeriodStatus.AUTO_RENEW,
|
||||
domain.getRepoId(),
|
||||
clock.nowUtc().plusDays(1),
|
||||
"foo",
|
||||
"TheRegistrar",
|
||||
recurringVKey))
|
||||
.build());
|
||||
doSuccessfulTest("domain_info_response_autorenewperiod.xml", false);
|
||||
doSuccessfulTest("domain_info_response_autorenewperiod.xml", false, ImmutableMap.of(), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -360,7 +404,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
|
||||
GracePeriodStatus.REDEMPTION,
|
||||
domain.getRepoId(),
|
||||
clock.nowUtc().plusDays(1),
|
||||
"foo",
|
||||
"TheRegistrar",
|
||||
null))
|
||||
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
|
||||
.build());
|
||||
@@ -379,7 +423,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
|
||||
GracePeriodStatus.RENEW,
|
||||
domain.getRepoId(),
|
||||
clock.nowUtc().plusDays(1),
|
||||
"foo",
|
||||
"TheRegistrar",
|
||||
null))
|
||||
.build());
|
||||
doSuccessfulTest("domain_info_response_renewperiod.xml", false);
|
||||
@@ -397,14 +441,14 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
|
||||
GracePeriodStatus.RENEW,
|
||||
domain.getRepoId(),
|
||||
clock.nowUtc().plusDays(1),
|
||||
"foo",
|
||||
"TheRegistrar",
|
||||
null))
|
||||
.addGracePeriod(
|
||||
GracePeriod.create(
|
||||
GracePeriodStatus.RENEW,
|
||||
domain.getRepoId(),
|
||||
clock.nowUtc().plusDays(2),
|
||||
"foo",
|
||||
"TheRegistrar",
|
||||
null))
|
||||
.build());
|
||||
doSuccessfulTest("domain_info_response_renewperiod.xml", false);
|
||||
@@ -422,7 +466,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
|
||||
GracePeriodStatus.TRANSFER,
|
||||
domain.getRepoId(),
|
||||
clock.nowUtc().plusDays(1),
|
||||
"foo",
|
||||
"TheRegistrar",
|
||||
null))
|
||||
.build());
|
||||
doSuccessfulTest("domain_info_response_transferperiod.xml", false);
|
||||
@@ -450,14 +494,14 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
|
||||
GracePeriodStatus.ADD,
|
||||
domain.getRepoId(),
|
||||
clock.nowUtc().plusDays(1),
|
||||
"foo",
|
||||
"TheRegistrar",
|
||||
null))
|
||||
.addGracePeriod(
|
||||
GracePeriod.create(
|
||||
GracePeriodStatus.RENEW,
|
||||
domain.getRepoId(),
|
||||
clock.nowUtc().plusDays(2),
|
||||
"foo",
|
||||
"TheRegistrar",
|
||||
null))
|
||||
.build());
|
||||
doSuccessfulTest("domain_info_response_stackedaddrenewperiod.xml", false);
|
||||
@@ -475,7 +519,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
|
||||
GracePeriodStatus.ADD,
|
||||
domain.getRepoId(),
|
||||
clock.nowUtc().plusDays(1),
|
||||
"foo",
|
||||
"TheRegistrar",
|
||||
null))
|
||||
.setDsData(
|
||||
ImmutableSet.of(
|
||||
@@ -929,4 +973,12 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
|
||||
assertIcannReportingActivityFieldLogged("srs-dom-info");
|
||||
assertTldsFieldLogged("tld");
|
||||
}
|
||||
|
||||
class SetClockExtension implements BeforeEachCallback {
|
||||
@Override
|
||||
public void beforeEach(ExtensionContext context) {
|
||||
// Kick the clock back to before the earliest date that we set the clock to.
|
||||
clock.setTo(DateTime.parse("2005-03-03T22:00:00.000Z"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,16 +70,23 @@ import google.registry.model.registry.Registry;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.testing.ReplayExtension;
|
||||
import java.util.Map;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link DomainRestoreRequestFlow}. */
|
||||
class DomainRestoreRequestFlowTest
|
||||
extends ResourceFlowTestCase<DomainRestoreRequestFlow, DomainBase> {
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
|
||||
private static final ImmutableMap<String, String> FEE_06_MAP =
|
||||
ImmutableMap.of("FEE_VERSION", "0.6", "FEE_NS", "fee", "CURRENCY", "USD");
|
||||
private static final ImmutableMap<String, String> FEE_11_MAP =
|
||||
@@ -99,11 +106,12 @@ class DomainRestoreRequestFlowTest
|
||||
}
|
||||
|
||||
void persistPendingDeleteDomain(DateTime expirationTime) throws Exception {
|
||||
DomainBase domain = newDomainBase(getUniqueIdFromCommand());
|
||||
DomainBase domain = persistResource(newDomainBase(getUniqueIdFromCommand()));
|
||||
HistoryEntry historyEntry =
|
||||
persistResource(
|
||||
new HistoryEntry.Builder()
|
||||
.setType(HistoryEntry.Type.DOMAIN_DELETE)
|
||||
.setModificationTime(clock.nowUtc())
|
||||
.setParent(domain)
|
||||
.build());
|
||||
persistResource(
|
||||
@@ -116,7 +124,7 @@ class DomainRestoreRequestFlowTest
|
||||
GracePeriodStatus.REDEMPTION,
|
||||
domain.getRepoId(),
|
||||
clock.nowUtc().plusDays(1),
|
||||
"foo",
|
||||
"TheRegistrar",
|
||||
null))
|
||||
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
|
||||
.setDeletePollMessage(
|
||||
|
||||
@@ -23,11 +23,11 @@ import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_REJECT;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST;
|
||||
import static google.registry.testing.DatabaseHelper.assertBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.deleteResource;
|
||||
import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType;
|
||||
import static google.registry.testing.DatabaseHelper.getOnlyPollMessage;
|
||||
import static google.registry.testing.DatabaseHelper.getPollMessages;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistDomainAsDeleted;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DomainBaseSubject.assertAboutDomains;
|
||||
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
|
||||
@@ -56,12 +56,14 @@ import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.model.transfer.TransferResponse;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link DomainTransferRejectFlow}. */
|
||||
@DualDatabaseTest
|
||||
class DomainTransferRejectFlowTest
|
||||
extends DomainTransferFlowTestCase<DomainTransferRejectFlow, DomainBase> {
|
||||
|
||||
@@ -151,31 +153,31 @@ class DomainTransferRejectFlowTest
|
||||
runFlow();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess() throws Exception {
|
||||
doSuccessfulTest("domain_transfer_reject.xml", "domain_transfer_reject_response.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testDryRun() throws Exception {
|
||||
setEppInput("domain_transfer_reject.xml");
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
dryRunFlowAssertResponse(loadFile("domain_transfer_reject_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_domainAuthInfo() throws Exception {
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_reject_domain_authinfo.xml", "domain_transfer_reject_response.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_contactAuthInfo() throws Exception {
|
||||
doSuccessfulTest(
|
||||
"domain_transfer_reject_contact_authinfo.xml", "domain_transfer_reject_response.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_notAuthorizedForTld() {
|
||||
persistResource(
|
||||
loadRegistrar("TheRegistrar").asBuilder().setAllowedTlds(ImmutableSet.of()).build());
|
||||
@@ -188,7 +190,7 @@ class DomainTransferRejectFlowTest
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_superuserNotAuthorizedForTld() throws Exception {
|
||||
persistResource(
|
||||
loadRegistrar("TheRegistrar").asBuilder().setAllowedTlds(ImmutableSet.of()).build());
|
||||
@@ -196,7 +198,7 @@ class DomainTransferRejectFlowTest
|
||||
CommitMode.LIVE, UserPrivileges.SUPERUSER, loadFile("domain_transfer_reject_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_badContactPassword() {
|
||||
// Change the contact's password so it does not match the password in the file.
|
||||
contact =
|
||||
@@ -212,7 +214,7 @@ class DomainTransferRejectFlowTest
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_badDomainPassword() {
|
||||
// Change the domain's password so it does not match the password in the file.
|
||||
domain =
|
||||
@@ -228,7 +230,7 @@ class DomainTransferRejectFlowTest
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_neverBeenTransferred() {
|
||||
changeTransferStatus(null);
|
||||
EppException thrown =
|
||||
@@ -237,7 +239,7 @@ class DomainTransferRejectFlowTest
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_clientApproved() {
|
||||
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
|
||||
EppException thrown =
|
||||
@@ -246,7 +248,7 @@ class DomainTransferRejectFlowTest
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_clientRejected() {
|
||||
changeTransferStatus(TransferStatus.CLIENT_REJECTED);
|
||||
EppException thrown =
|
||||
@@ -255,7 +257,7 @@ class DomainTransferRejectFlowTest
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_clientCancelled() {
|
||||
changeTransferStatus(TransferStatus.CLIENT_CANCELLED);
|
||||
EppException thrown =
|
||||
@@ -264,7 +266,7 @@ class DomainTransferRejectFlowTest
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_serverApproved() {
|
||||
changeTransferStatus(TransferStatus.SERVER_APPROVED);
|
||||
EppException thrown =
|
||||
@@ -273,7 +275,7 @@ class DomainTransferRejectFlowTest
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_serverCancelled() {
|
||||
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
|
||||
EppException thrown =
|
||||
@@ -282,7 +284,7 @@ class DomainTransferRejectFlowTest
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_gainingClient() {
|
||||
setClientIdForFlow("NewRegistrar");
|
||||
EppException thrown =
|
||||
@@ -291,7 +293,7 @@ class DomainTransferRejectFlowTest
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_unrelatedClient() {
|
||||
setClientIdForFlow("ClientZ");
|
||||
EppException thrown =
|
||||
@@ -300,7 +302,7 @@ class DomainTransferRejectFlowTest
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_deletedDomain() throws Exception {
|
||||
domain =
|
||||
persistResource(domain.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
|
||||
@@ -310,9 +312,9 @@ class DomainTransferRejectFlowTest
|
||||
assertThat(thrown).hasMessageThat().contains(String.format("(%s)", getUniqueIdFromCommand()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_nonexistentDomain() throws Exception {
|
||||
deleteResource(domain);
|
||||
persistDomainAsDeleted(domain, clock.nowUtc());
|
||||
ResourceDoesNotExistException thrown =
|
||||
assertThrows(
|
||||
ResourceDoesNotExistException.class, () -> doFailingTest("domain_transfer_reject.xml"));
|
||||
@@ -322,7 +324,7 @@ class DomainTransferRejectFlowTest
|
||||
// NB: No need to test pending delete status since pending transfers will get cancelled upon
|
||||
// entering pending delete phase. So it's already handled in that test case.
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testIcannActivityReportField_getsLogged() throws Exception {
|
||||
runFlow();
|
||||
assertIcannReportingActivityFieldLogged("srs-dom-transfer-reject");
|
||||
@@ -338,7 +340,7 @@ class DomainTransferRejectFlowTest
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testIcannTransactionRecord_noRecordsToCancel() throws Exception {
|
||||
setUpGracePeriodDurations();
|
||||
runFlow();
|
||||
@@ -348,7 +350,7 @@ class DomainTransferRejectFlowTest
|
||||
.containsExactly(DomainTransactionRecord.create("tld", clock.nowUtc(), TRANSFER_NACKED, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testIcannTransactionRecord_cancelsPreviousRecords() throws Exception {
|
||||
setUpGracePeriodDurations();
|
||||
DomainTransactionRecord previousSuccessRecord =
|
||||
|
||||
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.truth.Correspondence;
|
||||
import com.google.common.truth.Correspondence.BinaryPredicate;
|
||||
@@ -50,6 +51,11 @@ public final class ImmutableObjectSubject extends Subject {
|
||||
this.actual = actual;
|
||||
}
|
||||
|
||||
public void isEqualExceptFields(
|
||||
@Nullable ImmutableObject expected, Iterable<String> ignoredFields) {
|
||||
isEqualExceptFields(expected, Iterables.toArray(ignoredFields, String.class));
|
||||
}
|
||||
|
||||
public void isEqualExceptFields(@Nullable ImmutableObject expected, String... ignoredFields) {
|
||||
if (actual == null) {
|
||||
assertThat(expected).isNull();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.model.domain;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.flows.domain.DomainTransferUtils.createPendingTransferData;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
@@ -34,11 +35,13 @@ 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.Period.Unit;
|
||||
import google.registry.model.domain.launch.LaunchNotice;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
@@ -54,6 +57,7 @@ import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.testcontainers.shaded.com.google.common.collect.ImmutableList;
|
||||
|
||||
/** Verify that we can store/retrieve DomainBase objects from a SQL database. */
|
||||
@DualDatabaseTest
|
||||
@@ -617,15 +621,15 @@ public class DomainBaseSqlTest {
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
DomainTransferData transferData =
|
||||
new DomainTransferData.Builder()
|
||||
.setServerApproveBillingEvent(
|
||||
createLegacyVKey(BillingEvent.OneTime.class, oneTimeBillingEvent.getId()))
|
||||
.setServerApproveAutorenewEvent(
|
||||
createLegacyVKey(BillingEvent.Recurring.class, billEvent.getId()))
|
||||
.setServerApproveAutorenewPollMessage(
|
||||
createLegacyVKey(
|
||||
PollMessage.Autorenew.class, autorenewPollMessage.getId()))
|
||||
.build();
|
||||
createPendingTransferData(
|
||||
new DomainTransferData.Builder()
|
||||
.setTransferRequestTrid(Trid.create("foo", "bar"))
|
||||
.setTransferRequestTime(fakeClock.nowUtc())
|
||||
.setGainingClientId("registrar2")
|
||||
.setLosingClientId("registrar1")
|
||||
.setPendingTransferExpirationTime(fakeClock.nowUtc().plusDays(1)),
|
||||
ImmutableSet.of(oneTimeBillingEvent, billEvent, autorenewPollMessage),
|
||||
Period.create(0, Unit.YEARS));
|
||||
gracePeriods =
|
||||
ImmutableSet.of(
|
||||
GracePeriod.create(
|
||||
@@ -708,10 +712,17 @@ public class DomainBaseSqlTest {
|
||||
// Fix the original creation timestamp (this gets initialized on first write)
|
||||
DomainBase org = domain.asBuilder().setCreationTime(thatDomain.getCreationTime()).build();
|
||||
|
||||
String[] moreExcepts = Arrays.copyOf(excepts, excepts.length + 1);
|
||||
moreExcepts[moreExcepts.length - 1] = "updateTimestamp";
|
||||
|
||||
ImmutableList<String> moreExcepts =
|
||||
new ImmutableList.Builder<String>()
|
||||
.addAll(Arrays.asList(excepts))
|
||||
.add("updateTimestamp")
|
||||
.add("transferData")
|
||||
.build();
|
||||
// Note that the equality comparison forces a lazy load of all fields.
|
||||
assertAboutImmutableObjects().that(thatDomain).isEqualExceptFields(org, moreExcepts);
|
||||
// Transfer data cannot be directly compared due to serverApproveEtities inequalities
|
||||
assertAboutImmutableObjects()
|
||||
.that(domain.getTransferData())
|
||||
.isEqualExceptFields(org.getTransferData(), "serverApproveEntities");
|
||||
}
|
||||
}
|
||||
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.transaction;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.util.List;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Tests for {@link CriteriaQueryBuilder}. */
|
||||
class CriteriaQueryBuilderTest {
|
||||
|
||||
private final FakeClock fakeClock = new FakeClock();
|
||||
|
||||
private CriteriaQueryBuilderTestEntity entity1 =
|
||||
new CriteriaQueryBuilderTestEntity("name1", "data");
|
||||
private CriteriaQueryBuilderTestEntity entity2 =
|
||||
new CriteriaQueryBuilderTestEntity("name2", "zztz");
|
||||
private CriteriaQueryBuilderTestEntity entity3 = new CriteriaQueryBuilderTestEntity("zzz", "aaa");
|
||||
|
||||
@RegisterExtension
|
||||
final JpaUnitTestExtension jpaExtension =
|
||||
new JpaTestRules.Builder()
|
||||
.withClock(fakeClock)
|
||||
.withEntityClass(CriteriaQueryBuilderTestEntity.class)
|
||||
.buildUnitTestRule();
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
jpaTm().transact(() -> jpaTm().putAll(ImmutableList.of(entity1, entity2, entity3)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_noWhereClause() {
|
||||
assertThat(
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createQuery(
|
||||
CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class)
|
||||
.build())
|
||||
.getResultList()))
|
||||
.containsExactly(entity1, entity2, entity3)
|
||||
.inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_where_exactlyOne() {
|
||||
List<CriteriaQueryBuilderTestEntity> result =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
CriteriaQuery<CriteriaQueryBuilderTestEntity> query =
|
||||
CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class)
|
||||
.where(
|
||||
jpaTm().getEntityManager().getCriteriaBuilder()::equal,
|
||||
"data",
|
||||
"zztz")
|
||||
.build();
|
||||
return jpaTm().getEntityManager().createQuery(query).getResultList();
|
||||
});
|
||||
assertThat(result).containsExactly(entity2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_where_like_oneResult() {
|
||||
List<CriteriaQueryBuilderTestEntity> result =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
CriteriaQuery<CriteriaQueryBuilderTestEntity> query =
|
||||
CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class)
|
||||
.where(
|
||||
jpaTm().getEntityManager().getCriteriaBuilder()::like, "data", "a%")
|
||||
.build();
|
||||
return jpaTm().getEntityManager().createQuery(query).getResultList();
|
||||
});
|
||||
assertThat(result).containsExactly(entity3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_where_like_twoResults() {
|
||||
List<CriteriaQueryBuilderTestEntity> result =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
CriteriaQuery<CriteriaQueryBuilderTestEntity> query =
|
||||
CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class)
|
||||
.where(
|
||||
jpaTm().getEntityManager().getCriteriaBuilder()::like, "data", "%a%")
|
||||
.build();
|
||||
return jpaTm().getEntityManager().createQuery(query).getResultList();
|
||||
});
|
||||
assertThat(result).containsExactly(entity1, entity3).inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_multipleWheres() {
|
||||
List<CriteriaQueryBuilderTestEntity> result =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
CriteriaQuery<CriteriaQueryBuilderTestEntity> query =
|
||||
CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class)
|
||||
// first "where" matches 1 and 3
|
||||
.where(
|
||||
jpaTm().getEntityManager().getCriteriaBuilder()::like, "data", "%a%")
|
||||
// second "where" matches 1 and 2
|
||||
.where(
|
||||
jpaTm().getEntityManager().getCriteriaBuilder()::like, "data", "%t%")
|
||||
.build();
|
||||
return jpaTm().getEntityManager().createQuery(query).getResultList();
|
||||
});
|
||||
assertThat(result).containsExactly(entity1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_where_in_oneResult() {
|
||||
List<CriteriaQueryBuilderTestEntity> result =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
CriteriaQuery<CriteriaQueryBuilderTestEntity> query =
|
||||
CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class)
|
||||
.whereFieldIsIn("data", ImmutableList.of("aaa", "bbb"))
|
||||
.build();
|
||||
return jpaTm().getEntityManager().createQuery(query).getResultList();
|
||||
});
|
||||
assertThat(result).containsExactly(entity3).inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_where_in_twoResults() {
|
||||
List<CriteriaQueryBuilderTestEntity> result =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
CriteriaQuery<CriteriaQueryBuilderTestEntity> query =
|
||||
CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class)
|
||||
.whereFieldIsIn("data", ImmutableList.of("aaa", "bbb", "data"))
|
||||
.build();
|
||||
return jpaTm().getEntityManager().createQuery(query).getResultList();
|
||||
});
|
||||
assertThat(result).containsExactly(entity1, entity3).inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_orderBy() {
|
||||
List<CriteriaQueryBuilderTestEntity> result =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
CriteriaQuery<CriteriaQueryBuilderTestEntity> query =
|
||||
CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class)
|
||||
.orderBy(jpaTm().getEntityManager().getCriteriaBuilder()::asc, "data")
|
||||
.where(
|
||||
jpaTm().getEntityManager().getCriteriaBuilder()::like, "data", "%a%")
|
||||
.build();
|
||||
return jpaTm().getEntityManager().createQuery(query).getResultList();
|
||||
});
|
||||
assertThat(result).containsExactly(entity3, entity1).inOrder();
|
||||
}
|
||||
|
||||
@Entity(name = "CriteriaQueryBuilderTestEntity")
|
||||
private static class CriteriaQueryBuilderTestEntity extends ImmutableObject {
|
||||
@Id private String name;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private String data;
|
||||
|
||||
private CriteriaQueryBuilderTestEntity() {}
|
||||
|
||||
private CriteriaQueryBuilderTestEntity(String name, String data) {
|
||||
this.name = name;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ class TransactionTest {
|
||||
});
|
||||
TransactionEntity txnEnt =
|
||||
jpaTm().transact(() -> jpaTm().loadByKey(VKey.createSql(TransactionEntity.class, 1L)));
|
||||
Transaction txn = Transaction.deserialize(txnEnt.contents);
|
||||
Transaction txn = Transaction.deserialize(txnEnt.getContents());
|
||||
txn.writeToDatastore();
|
||||
ofyTm()
|
||||
.transact(
|
||||
|
||||
@@ -43,14 +43,16 @@ import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.rdap.RdapMetrics.EndpointType;
|
||||
import google.registry.rdap.RdapMetrics.SearchType;
|
||||
import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link RdapEntitySearchAction}. */
|
||||
@DualDatabaseTest
|
||||
class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySearchAction> {
|
||||
|
||||
RdapEntitySearchActionTest() {
|
||||
@@ -209,9 +211,9 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
.asBuilder()
|
||||
.setRepoId(String.format("%04d-ROID", i))
|
||||
.build();
|
||||
resourcesBuilder.add(contact);
|
||||
resourcesBuilder.add(makeHistoryEntry(
|
||||
contact, HistoryEntry.Type.CONTACT_CREATE, null, "created", clock.nowUtc()));
|
||||
resourcesBuilder.add(contact);
|
||||
}
|
||||
persistResources(resourcesBuilder.build());
|
||||
for (int i = 1; i <= numRegistrars; i++) {
|
||||
@@ -386,7 +388,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testInvalidPath_rejected() {
|
||||
action.requestPath = actionPath + "/path";
|
||||
action.run();
|
||||
@@ -394,7 +396,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(Optional.empty(), 400);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testInvalidRequest_rejected() {
|
||||
action.run();
|
||||
assertThat(parseJsonObject(response.getPayload()))
|
||||
@@ -405,7 +407,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(Optional.empty(), 400);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatch_suffixRejected() {
|
||||
assertThat(generateActualJsonWithFullName("exam*ple"))
|
||||
.isEqualTo(
|
||||
@@ -417,7 +419,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(Optional.empty(), 422);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatch_suffixRejected() {
|
||||
assertThat(generateActualJsonWithHandle("exam*ple"))
|
||||
.isEqualTo(
|
||||
@@ -429,7 +431,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(Optional.empty(), 422);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testMultipleWildcards_rejected() {
|
||||
assertThat(generateActualJsonWithHandle("*.*"))
|
||||
.isEqualTo(
|
||||
@@ -441,7 +443,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(Optional.empty(), 422);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNoCharactersToMatch_rejected() {
|
||||
rememberWildcardType("*");
|
||||
assertThat(generateActualJsonWithHandle("*"))
|
||||
@@ -453,7 +455,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(Optional.empty(), 422);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFewerThanTwoCharactersToMatch_rejected() {
|
||||
rememberWildcardType("a*");
|
||||
assertThat(generateActualJsonWithHandle("a*"))
|
||||
@@ -465,7 +467,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(Optional.empty(), 422);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testInvalidSubtype_rejected() {
|
||||
action.subtypeParam = Optional.of("Space Aliens");
|
||||
assertThat(generateActualJsonWithFullName("Blinky (赤ベイ)"))
|
||||
@@ -478,14 +480,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(Optional.empty(), 400);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_found() {
|
||||
login("2-RegistrarTest");
|
||||
runSuccessfulNameTestWithBlinky("Blinky (赤ベイ)", "rdap_contact.json");
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_found_subtypeAll() {
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("aLl");
|
||||
@@ -493,7 +495,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_found_subtypeContacts() {
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("cONTACTS");
|
||||
@@ -501,7 +503,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_notFound_subtypeRegistrars() {
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("Registrars");
|
||||
@@ -509,7 +511,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_found_specifyingSameRegistrar() {
|
||||
login("2-RegistrarTest");
|
||||
action.registrarParam = Optional.of("2-RegistrarTest");
|
||||
@@ -517,7 +519,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_notFound_specifyingOtherRegistrar() {
|
||||
login("2-RegistrarTest");
|
||||
action.registrarParam = Optional.of("2-RegistrarInact");
|
||||
@@ -525,7 +527,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_found_asAdministrator() {
|
||||
loginAsAdmin();
|
||||
rememberWildcardType("Blinky (赤ベイ)");
|
||||
@@ -533,27 +535,27 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_notFound_notLoggedIn() {
|
||||
runNotFoundNameTest("Blinky (赤ベイ)");
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_notFound_loggedInAsOtherRegistrar() {
|
||||
login("2-Registrar");
|
||||
runNotFoundNameTest("Blinky (赤ベイ)");
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_found_wildcard() {
|
||||
login("2-RegistrarTest");
|
||||
runSuccessfulNameTestWithBlinky("Blinky*", "rdap_contact.json");
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_found_wildcardSpecifyingSameRegistrar() {
|
||||
login("2-RegistrarTest");
|
||||
action.registrarParam = Optional.of("2-RegistrarTest");
|
||||
@@ -561,7 +563,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_notFound_wildcardSpecifyingOtherRegistrar() {
|
||||
login("2-RegistrarTest");
|
||||
action.registrarParam = Optional.of("2-RegistrarInact");
|
||||
@@ -569,7 +571,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_found_wildcardBoth() {
|
||||
login("2-RegistrarTest");
|
||||
rememberWildcardType("Blin*");
|
||||
@@ -579,14 +581,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_notFound_deleted() {
|
||||
login("2-RegistrarTest");
|
||||
runNotFoundNameTest("Cl*");
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_notFound_deletedWhenLoggedInAsOtherRegistrar() {
|
||||
login("2-RegistrarTest");
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
@@ -594,7 +596,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_notFound_deletedWhenLoggedInAsSameRegistrar() {
|
||||
login("2-Registrar");
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
@@ -602,7 +604,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContact_notFound_deletedWhenLoggedInAsAdmin() {
|
||||
loginAsAdmin();
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
@@ -610,7 +612,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrar_found() {
|
||||
login("2-RegistrarTest");
|
||||
runSuccessfulNameTest(
|
||||
@@ -618,7 +620,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrar_found_subtypeAll() {
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("all");
|
||||
@@ -627,7 +629,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrar_found_subtypeRegistrars() {
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("REGISTRARS");
|
||||
@@ -636,7 +638,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrar_notFound_subtypeContacts() {
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("contacts");
|
||||
@@ -644,7 +646,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrar_found_specifyingSameRegistrar() {
|
||||
action.registrarParam = Optional.of("2-Registrar");
|
||||
runSuccessfulNameTest(
|
||||
@@ -652,25 +654,26 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrar_notFound_specifyingDifferentRegistrar() {
|
||||
action.registrarParam = Optional.of("2-RegistrarTest");
|
||||
runNotFoundNameTest("Yes Virginia <script>");
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContacts_nonTruncated() {
|
||||
login("2-RegistrarTest");
|
||||
createManyContactsAndRegistrars(4, 0, registrarTest);
|
||||
rememberWildcardType("Entity *");
|
||||
// JsonObject foo = generateActualJsonWithFullName("Entity *");
|
||||
assertThat(generateActualJsonWithFullName("Entity *"))
|
||||
.isEqualTo(generateExpectedJson("rdap_nontruncated_contacts.json"));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContacts_truncated() {
|
||||
login("2-RegistrarTest");
|
||||
createManyContactsAndRegistrars(5, 0, registrarTest);
|
||||
@@ -683,7 +686,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(5, IncompletenessWarningType.TRUNCATED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContacts_reallyTruncated() {
|
||||
login("2-RegistrarTest");
|
||||
createManyContactsAndRegistrars(9, 0, registrarTest);
|
||||
@@ -697,7 +700,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(5, IncompletenessWarningType.TRUNCATED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchContacts_cursorNavigation() throws Exception {
|
||||
login("2-RegistrarTest");
|
||||
createManyContactsAndRegistrars(9, 0, registrarTest);
|
||||
@@ -716,7 +719,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
"Entity 9"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrars_nonTruncated() {
|
||||
createManyContactsAndRegistrars(0, 4, registrarTest);
|
||||
rememberWildcardType("Entity *");
|
||||
@@ -726,7 +729,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrars_truncated() {
|
||||
createManyContactsAndRegistrars(0, 5, registrarTest);
|
||||
rememberWildcardType("Entity *");
|
||||
@@ -738,7 +741,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(0, IncompletenessWarningType.TRUNCATED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrars_reallyTruncated() {
|
||||
createManyContactsAndRegistrars(0, 9, registrarTest);
|
||||
rememberWildcardType("Entity *");
|
||||
@@ -750,7 +753,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(0, IncompletenessWarningType.TRUNCATED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrars_cursorNavigation() throws Exception {
|
||||
createManyContactsAndRegistrars(0, 13, registrarTest);
|
||||
checkCursorNavigation(
|
||||
@@ -772,7 +775,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
"Entity 9"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrars_cursorNavigationThroughAll() throws Exception {
|
||||
createManyContactsAndRegistrars(0, 13, registrarTest);
|
||||
action.subtypeParam = Optional.of("registrars");
|
||||
@@ -798,7 +801,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
"Yes Virginia <script>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchMix_truncated() {
|
||||
login("2-RegistrarTest");
|
||||
createManyContactsAndRegistrars(3, 3, registrarTest);
|
||||
@@ -811,7 +814,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(3, IncompletenessWarningType.TRUNCATED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchMix_cursorNavigation() throws Exception {
|
||||
login("2-RegistrarTest");
|
||||
createManyContactsAndRegistrars(3, 3, registrarTest);
|
||||
@@ -827,7 +830,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
"Entity 6"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchMix_subtypeContacts() {
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("contacts");
|
||||
@@ -839,7 +842,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchMix_subtypeRegistrars() {
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("registrars");
|
||||
@@ -849,13 +852,13 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrar_notFound_inactive() {
|
||||
runNotFoundNameTest("No Way");
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrar_notFound_inactiveAsDifferentRegistrar() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
login("2-Registrar");
|
||||
@@ -863,7 +866,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrar_found_inactiveAsSameRegistrar() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
login("2-RegistrarInact");
|
||||
@@ -871,7 +874,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrar_found_inactiveAsAdmin() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
loginAsAdmin();
|
||||
@@ -879,13 +882,13 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrar_notFound_test() {
|
||||
runNotFoundNameTest("Da Test Registrar");
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrar_notFound_testAsDifferentRegistrar() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
login("2-Registrar");
|
||||
@@ -893,7 +896,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrar_found_testAsSameRegistrar() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
login("2-RegistrarTest");
|
||||
@@ -902,7 +905,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testNameMatchRegistrar_found_testAsAdmin() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
loginAsAdmin();
|
||||
@@ -911,14 +914,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_found() {
|
||||
login("2-RegistrarTest");
|
||||
runSuccessfulHandleTestWithBlinky("2-ROID", "rdap_contact.json");
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_found_subtypeAll() {
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("all");
|
||||
@@ -926,7 +929,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_found_subtypeContacts() {
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("contacts");
|
||||
@@ -934,7 +937,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_notFound_subtypeRegistrars() {
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("reGistrars");
|
||||
@@ -942,28 +945,28 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_found_specifyingSameRegistrar() {
|
||||
action.registrarParam = Optional.of("2-RegistrarTest");
|
||||
runSuccessfulHandleTestWithBlinky("2-ROID", "rdap_contact_no_personal_data_with_remark.json");
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_notFound_specifyingDifferentRegistrar() {
|
||||
action.registrarParam = Optional.of("2-Registrar");
|
||||
runNotFoundHandleTest("2-ROID");
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_notFound_deleted() {
|
||||
login("2-RegistrarTest");
|
||||
runNotFoundHandleTest("6-ROID");
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_notFound_deletedWhenLoggedInAsOtherRegistrar() {
|
||||
login("2-RegistrarTest");
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
@@ -971,7 +974,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_found_deletedWhenLoggedInAsSameRegistrar() {
|
||||
login("2-Registrar");
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
@@ -985,7 +988,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_found_deletedWhenLoggedInAsAdmin() {
|
||||
loginAsAdmin();
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
@@ -999,14 +1002,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_notFound_deletedWildcard() {
|
||||
login("2-RegistrarTest");
|
||||
runNotFoundHandleTest("6-ROI*");
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_notFound_deletedWildcardWhenLoggedInAsOtherRegistrar() {
|
||||
login("2-RegistrarTest");
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
@@ -1014,7 +1017,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_found_deletedWildcardWhenLoggedInAsSameRegistrar() {
|
||||
login("2-Registrar");
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
@@ -1028,7 +1031,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_found_deletedWildcardWhenLoggedInAsAdmin() {
|
||||
loginAsAdmin();
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
@@ -1042,48 +1045,48 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchRegistrar_found() {
|
||||
runSuccessfulHandleTest("20", "20", "Yes Virginia <script>", "rdap_registrar.json");
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchRegistrar_found_subtypeAll() {
|
||||
action.subtypeParam = Optional.of("all");
|
||||
runSuccessfulHandleTest("20", "20", "Yes Virginia <script>", "rdap_registrar.json");
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchRegistrar_found_subtypeRegistrars() {
|
||||
action.subtypeParam = Optional.of("registrars");
|
||||
runSuccessfulHandleTest("20", "20", "Yes Virginia <script>", "rdap_registrar.json");
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchRegistrar_notFound_subtypeContacts() {
|
||||
action.subtypeParam = Optional.of("contacts");
|
||||
runNotFoundHandleTest("20");
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchRegistrar_found_specifyingSameRegistrar() {
|
||||
action.registrarParam = Optional.of("2-Registrar");
|
||||
runSuccessfulHandleTest("20", "20", "Yes Virginia <script>", "rdap_registrar.json");
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchRegistrar_notFound_specifyingDifferentRegistrar() {
|
||||
action.registrarParam = Optional.of("2-RegistrarTest");
|
||||
runNotFoundHandleTest("20");
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_found_wildcardWithResultSetSizeOne() {
|
||||
login("2-RegistrarTest");
|
||||
action.rdapResultSetMaxSize = 1;
|
||||
@@ -1091,14 +1094,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_found_wildcard() {
|
||||
login("2-RegistrarTest");
|
||||
runSuccessfulHandleTestWithBlinky("2-RO*", "rdap_contact.json");
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_found_wildcardSpecifyingSameRegistrar() {
|
||||
action.registrarParam = Optional.of("2-RegistrarTest");
|
||||
login("2-RegistrarTest");
|
||||
@@ -1106,7 +1109,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_notFound_wildcardSpecifyingDifferentRegistrar() {
|
||||
action.registrarParam = Optional.of("2-Registrar");
|
||||
login("2-RegistrarTest");
|
||||
@@ -1114,20 +1117,20 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_found_deleted() {
|
||||
login("2-RegistrarTest");
|
||||
runSuccessfulHandleTestWithBlinky("2-RO*", "rdap_contact.json");
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_notFound_wildcard() {
|
||||
runNotFoundHandleTest("20*");
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_cursorNavigationWithFullLastPage() throws Exception {
|
||||
login("2-RegistrarTest");
|
||||
createManyContactsAndRegistrars(12, 0, registrarTest);
|
||||
@@ -1150,7 +1153,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
"Entity 12"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchContact_cursorNavigationWithPartialLastPage() throws Exception {
|
||||
login("2-RegistrarTest");
|
||||
createManyContactsAndRegistrars(13, 0, registrarTest);
|
||||
@@ -1174,13 +1177,13 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
"Entity 13"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchRegistrar_notFound_wildcard() {
|
||||
runNotFoundHandleTest("3test*");
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchRegistrars_cursorNavigationThroughAll() throws Exception {
|
||||
createManyContactsAndRegistrars(0, 13, registrarTest);
|
||||
action.subtypeParam = Optional.of("registrars");
|
||||
@@ -1206,7 +1209,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
"Yes Virginia <script>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchMix_found_truncated() {
|
||||
createManyContactsAndRegistrars(30, 0, registrarTest);
|
||||
rememberWildcardType("00*");
|
||||
@@ -1216,13 +1219,13 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(5, IncompletenessWarningType.TRUNCATED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchRegistrar_notFound_inactive() {
|
||||
runNotFoundHandleTest("21");
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchRegistrar_notFound_inactiveAsDifferentRegistrar() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
login("2-Registrar");
|
||||
@@ -1230,7 +1233,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchRegistrar_found_inactiveAsSameRegistrar() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
login("2-RegistrarInact");
|
||||
@@ -1238,7 +1241,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testHandleMatchRegistrar_found_inactiveAsAdmin() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
loginAsAdmin();
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.schema.replay;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
|
||||
import static google.registry.testing.LogsSubject.assertAboutLogs;
|
||||
|
||||
import com.google.common.testing.TestLogHandler;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.TransactionEntity;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
public class ReplicateToDatastoreActionTest {
|
||||
|
||||
@RegisterExtension
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder()
|
||||
.withDatastoreAndCloudSql()
|
||||
.withOfyTestEntities(TestEntity.class)
|
||||
.withJpaUnitTestEntities(TestEntity.class)
|
||||
.build();
|
||||
|
||||
ReplicateToDatastoreAction task = new ReplicateToDatastoreAction();
|
||||
|
||||
TestLogHandler logHandler;
|
||||
|
||||
public ReplicateToDatastoreActionTest() {}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
RegistryConfig.overrideCloudSqlReplicateTransactions(true);
|
||||
logHandler = new TestLogHandler();
|
||||
Logger.getLogger(ReplicateToDatastoreAction.class.getCanonicalName()).addHandler(logHandler);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
RegistryConfig.overrideCloudSqlReplicateTransactions(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplication() {
|
||||
TestEntity foo = new TestEntity("foo");
|
||||
TestEntity bar = new TestEntity("bar");
|
||||
TestEntity baz = new TestEntity("baz");
|
||||
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
jpaTm().insert(foo);
|
||||
jpaTm().insert(bar);
|
||||
});
|
||||
task.run();
|
||||
|
||||
assertThat(ofyTm().transact(() -> ofyTm().loadByKey(foo.key()))).isEqualTo(foo);
|
||||
assertThat(ofyTm().transact(() -> ofyTm().loadByKey(bar.key()))).isEqualTo(bar);
|
||||
assertThat(ofyTm().transact(() -> ofyTm().loadByKeyIfPresent(baz.key())).isPresent()).isFalse();
|
||||
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
jpaTm().delete(bar.key());
|
||||
jpaTm().insert(baz);
|
||||
});
|
||||
task.run();
|
||||
|
||||
assertThat(ofyTm().transact(() -> ofyTm().loadByKeyIfPresent(bar.key()).isPresent())).isFalse();
|
||||
assertThat(ofyTm().transact(() -> ofyTm().loadByKey(baz.key()))).isEqualTo(baz);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplayFromLastTxn() {
|
||||
TestEntity foo = new TestEntity("foo");
|
||||
TestEntity bar = new TestEntity("bar");
|
||||
|
||||
// Write a transaction containing "foo".
|
||||
jpaTm().transact(() -> jpaTm().insert(foo));
|
||||
task.run();
|
||||
|
||||
// Verify that it propagated to datastore, then remove "foo" directly from datastore.
|
||||
assertThat(ofyTm().transact(() -> ofyTm().loadByKey(foo.key()))).isEqualTo(foo);
|
||||
ofyTm().transact(() -> ofyTm().delete(foo.key()));
|
||||
|
||||
// Write "bar"
|
||||
jpaTm().transact(() -> jpaTm().insert(bar));
|
||||
task.run();
|
||||
|
||||
// If we replayed only the most recent transaction, we should have "bar" but not "foo".
|
||||
assertThat(ofyTm().transact(() -> ofyTm().loadByKey(bar.key()))).isEqualTo(bar);
|
||||
assertThat(ofyTm().transact(() -> ofyTm().loadByKeyIfPresent(foo.key()).isPresent())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnintentionalConcurrency() {
|
||||
TestEntity foo = new TestEntity("foo");
|
||||
TestEntity bar = new TestEntity("bar");
|
||||
|
||||
// Write a transaction and run just the batch fetch.
|
||||
jpaTm().transact(() -> jpaTm().insert(foo));
|
||||
List<TransactionEntity> txns1 = task.getTransactionBatch();
|
||||
assertThat(txns1).hasSize(1);
|
||||
|
||||
// Write a second transaction and do another batch fetch.
|
||||
jpaTm().transact(() -> jpaTm().insert(bar));
|
||||
List<TransactionEntity> txns2 = task.getTransactionBatch();
|
||||
assertThat(txns2).hasSize(2);
|
||||
|
||||
// Apply the first batch.
|
||||
assertThat(task.applyTransaction(txns1.get(0))).isFalse();
|
||||
|
||||
// Remove the foo record so we can ensure that this transaction doesn't get doublle-played.
|
||||
ofyTm().transact(() -> ofyTm().delete(foo.key()));
|
||||
|
||||
// Apply the second batch.
|
||||
for (TransactionEntity txn : txns2) {
|
||||
assertThat(task.applyTransaction(txn)).isFalse();
|
||||
}
|
||||
|
||||
// Verify that the first transaction didn't get replayed but the second one did.
|
||||
assertThat(ofyTm().transact(() -> ofyTm().loadByKeyIfPresent(foo.key()).isPresent())).isFalse();
|
||||
assertThat(ofyTm().transact(() -> ofyTm().loadByKey(bar.key()))).isEqualTo(bar);
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.WARNING, "Ignoring transaction 1, which appears to have already been applied.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingTransactions() {
|
||||
// Write a transaction (should have a transaction id of 1).
|
||||
TestEntity foo = new TestEntity("foo");
|
||||
jpaTm().transact(() -> jpaTm().insert(foo));
|
||||
|
||||
// Force the last transaction id back to -1 so that we look for transaction 0.
|
||||
ofyTm().transact(() -> ofyTm().insert(new LastSqlTransaction(-1)));
|
||||
|
||||
List<TransactionEntity> txns = task.getTransactionBatch();
|
||||
assertThat(txns).hasSize(1);
|
||||
assertThat(task.applyTransaction(txns.get(0))).isTrue();
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.SEVERE,
|
||||
"Missing transaction: last transaction id = -1, next available transaction = 1");
|
||||
}
|
||||
|
||||
@Entity(name = "ReplicationTestEntity")
|
||||
@javax.persistence.Entity(name = "TestEntity")
|
||||
private static class TestEntity extends ImmutableObject {
|
||||
@Id @javax.persistence.Id private String name;
|
||||
|
||||
private TestEntity() {}
|
||||
|
||||
private TestEntity(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public VKey<TestEntity> key() {
|
||||
return VKey.create(TestEntity.class, name, Key.create(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,12 @@ package google.registry.testing;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.io.Files.asCharSink;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResources;
|
||||
import static google.registry.testing.DualDatabaseTestInvocationContextProvider.injectTmForDualDatabaseTest;
|
||||
import static google.registry.testing.DualDatabaseTestInvocationContextProvider.restoreTmAfterDualDatabaseTest;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static google.registry.util.ResourceUtils.readResourceUtf8;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
@@ -40,10 +42,16 @@ import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.io.Files;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.ObjectifyFilter;
|
||||
import google.registry.model.common.DatabaseTransitionSchedule;
|
||||
import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabase;
|
||||
import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabaseTransition;
|
||||
import google.registry.model.common.DatabaseTransitionSchedule.TransitionId;
|
||||
import google.registry.model.common.TimedTransitionProperty;
|
||||
import google.registry.model.ofy.ObjectifyService;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.Registrar.State;
|
||||
@@ -390,6 +398,14 @@ public final class AppEngineExtension implements BeforeEachCallback, AfterEachCa
|
||||
loadInitialData();
|
||||
}
|
||||
} else {
|
||||
// If we're using SQL, set replayed entities to use SQL
|
||||
DatabaseTransitionSchedule schedule =
|
||||
DatabaseTransitionSchedule.create(
|
||||
TransitionId.REPLAYED_ENTITIES,
|
||||
TimedTransitionProperty.fromValueMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, PrimaryDatabase.CLOUD_SQL),
|
||||
PrimaryDatabaseTransition.class));
|
||||
tm().transactNew(() -> ofy().saveWithoutBackup().entity(schedule).now());
|
||||
if (withCloudSql && !withJpaUnitTest && !withoutCannedData) {
|
||||
loadInitialData();
|
||||
}
|
||||
|
||||
@@ -326,8 +326,7 @@ public class DatabaseHelper {
|
||||
* Returns a persisted domain that is the passed-in domain modified to be deleted at the specified
|
||||
* time.
|
||||
*/
|
||||
public static DomainBase persistDomainAsDeleted(
|
||||
DomainBase domain, DateTime deletionTime) {
|
||||
public static DomainBase persistDomainAsDeleted(DomainBase domain, DateTime deletionTime) {
|
||||
return persistResource(domain.asBuilder().setDeletionTime(deletionTime).build());
|
||||
}
|
||||
|
||||
@@ -347,7 +346,7 @@ public class DatabaseHelper {
|
||||
}
|
||||
|
||||
public static ReservedList persistReservedList(
|
||||
String listName, boolean shouldPublish, String... lines) {
|
||||
String listName, boolean shouldPublish, String... lines) {
|
||||
ReservedList reservedList =
|
||||
new ReservedList.Builder()
|
||||
.setName(listName)
|
||||
@@ -512,10 +511,7 @@ public class DatabaseHelper {
|
||||
}
|
||||
|
||||
public static BillingEvent.OneTime createBillingEventForTransfer(
|
||||
DomainBase domain,
|
||||
HistoryEntry historyEntry,
|
||||
DateTime costLookupTime,
|
||||
DateTime eventTime) {
|
||||
DomainBase domain, HistoryEntry historyEntry, DateTime costLookupTime, DateTime eventTime) {
|
||||
return new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.TRANSFER)
|
||||
.setTargetId(domain.getDomainName())
|
||||
@@ -530,10 +526,7 @@ public class DatabaseHelper {
|
||||
}
|
||||
|
||||
public static ContactResource persistContactWithPendingTransfer(
|
||||
ContactResource contact,
|
||||
DateTime requestTime,
|
||||
DateTime expirationTime,
|
||||
DateTime now) {
|
||||
ContactResource contact, DateTime requestTime, DateTime expirationTime, DateTime now) {
|
||||
HistoryEntry historyEntryContactTransfer =
|
||||
persistResource(
|
||||
new HistoryEntry.Builder()
|
||||
@@ -587,26 +580,29 @@ public class DatabaseHelper {
|
||||
String domainName = String.format("%s.%s", label, tld);
|
||||
String repoId = generateNewDomainRoid(tld);
|
||||
DomainBase domain =
|
||||
new DomainBase.Builder()
|
||||
.setRepoId(repoId)
|
||||
.setDomainName(domainName)
|
||||
.setPersistedCurrentSponsorClientId("TheRegistrar")
|
||||
.setCreationClientId("TheRegistrar")
|
||||
.setCreationTimeForTest(creationTime)
|
||||
.setRegistrationExpirationTime(expirationTime)
|
||||
.setRegistrant(contact.createVKey())
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, contact.createVKey()),
|
||||
DesignatedContact.create(Type.TECH, contact.createVKey())))
|
||||
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("fooBAR")))
|
||||
.addGracePeriod(
|
||||
GracePeriod.create(GracePeriodStatus.ADD, repoId, now.plusDays(10), "foo", null))
|
||||
.build();
|
||||
HistoryEntry historyEntryDomainCreate =
|
||||
persistResource(
|
||||
new HistoryEntry.Builder()
|
||||
new DomainBase.Builder()
|
||||
.setRepoId(repoId)
|
||||
.setDomainName(domainName)
|
||||
.setPersistedCurrentSponsorClientId("TheRegistrar")
|
||||
.setCreationClientId("TheRegistrar")
|
||||
.setCreationTimeForTest(creationTime)
|
||||
.setRegistrationExpirationTime(expirationTime)
|
||||
.setRegistrant(contact.createVKey())
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, contact.createVKey()),
|
||||
DesignatedContact.create(Type.TECH, contact.createVKey())))
|
||||
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("fooBAR")))
|
||||
.addGracePeriod(
|
||||
GracePeriod.create(
|
||||
GracePeriodStatus.ADD, repoId, now.plusDays(10), "TheRegistrar", null))
|
||||
.build());
|
||||
DomainHistory historyEntryDomainCreate =
|
||||
persistResource(
|
||||
new DomainHistory.Builder()
|
||||
.setType(HistoryEntry.Type.DOMAIN_CREATE)
|
||||
.setModificationTime(now)
|
||||
.setParent(domain)
|
||||
.build());
|
||||
BillingEvent.Recurring autorenewEvent =
|
||||
@@ -643,18 +639,17 @@ public class DatabaseHelper {
|
||||
DateTime requestTime,
|
||||
DateTime expirationTime,
|
||||
DateTime extendedRegistrationExpirationTime) {
|
||||
HistoryEntry historyEntryDomainTransfer =
|
||||
DomainHistory historyEntryDomainTransfer =
|
||||
persistResource(
|
||||
new HistoryEntry.Builder()
|
||||
new DomainHistory.Builder()
|
||||
.setType(HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST)
|
||||
.setModificationTime(tm().transact(() -> tm().getTransactionTime()))
|
||||
.setParent(domain)
|
||||
.build());
|
||||
BillingEvent.OneTime transferBillingEvent = persistResource(createBillingEventForTransfer(
|
||||
domain,
|
||||
historyEntryDomainTransfer,
|
||||
requestTime,
|
||||
expirationTime));
|
||||
BillingEvent.OneTime transferBillingEvent =
|
||||
persistResource(
|
||||
createBillingEventForTransfer(
|
||||
domain, historyEntryDomainTransfer, requestTime, expirationTime));
|
||||
BillingEvent.Recurring gainingClientAutorenewEvent =
|
||||
persistResource(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
@@ -678,18 +673,18 @@ public class DatabaseHelper {
|
||||
.build());
|
||||
// Modify the existing autorenew event to reflect the pending transfer.
|
||||
persistResource(
|
||||
tm().loadByKey(domain.getAutorenewBillingEvent())
|
||||
.asBuilder()
|
||||
.setRecurrenceEndTime(expirationTime)
|
||||
.build());
|
||||
transactIfJpaTm(
|
||||
() ->
|
||||
tm().loadByKey(domain.getAutorenewBillingEvent())
|
||||
.asBuilder()
|
||||
.setRecurrenceEndTime(expirationTime)
|
||||
.build()));
|
||||
// Update the end time of the existing autorenew poll message. We must delete it if it has no
|
||||
// events left in it.
|
||||
PollMessage.Autorenew autorenewPollMessage = tm().loadByKey(domain.getAutorenewPollMessage());
|
||||
PollMessage.Autorenew autorenewPollMessage =
|
||||
transactIfJpaTm(() -> tm().loadByKey(domain.getAutorenewPollMessage()));
|
||||
if (autorenewPollMessage.getEventTime().isBefore(expirationTime)) {
|
||||
persistResource(
|
||||
autorenewPollMessage.asBuilder()
|
||||
.setAutorenewEndTime(expirationTime)
|
||||
.build());
|
||||
persistResource(autorenewPollMessage.asBuilder().setAutorenewEndTime(expirationTime).build());
|
||||
} else {
|
||||
deleteResource(autorenewPollMessage);
|
||||
}
|
||||
@@ -928,11 +923,8 @@ public class DatabaseHelper {
|
||||
}
|
||||
|
||||
public static PollMessage getOnlyPollMessage(
|
||||
String clientId,
|
||||
DateTime now,
|
||||
Class<? extends PollMessage> subType) {
|
||||
return getPollMessages(clientId, now)
|
||||
.stream()
|
||||
String clientId, DateTime now, Class<? extends PollMessage> subType) {
|
||||
return getPollMessages(clientId, now).stream()
|
||||
.filter(subType::isInstance)
|
||||
.map(subType::cast)
|
||||
.collect(onlyElement());
|
||||
@@ -957,10 +949,7 @@ public class DatabaseHelper {
|
||||
return createDomainRepoId(ObjectifyService.allocateId(), tld);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a newly allocated, globally unique contact/host repoId of the format
|
||||
* HEX_TLD-ROID.
|
||||
*/
|
||||
/** Returns a newly allocated, globally unique contact/host repoId of the format HEX_TLD-ROID. */
|
||||
public static String generateNewContactHostRoid() {
|
||||
return createRepoId(ObjectifyService.allocateId(), getContactAndHostRoidSuffix());
|
||||
}
|
||||
@@ -1131,8 +1120,7 @@ public class DatabaseHelper {
|
||||
*/
|
||||
public static ImmutableList<HistoryEntry> getHistoryEntriesOfType(
|
||||
EppResource resource, final HistoryEntry.Type type) {
|
||||
return getHistoryEntries(resource)
|
||||
.stream()
|
||||
return getHistoryEntries(resource).stream()
|
||||
.filter(entry -> entry.getType() == type)
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
@@ -1143,7 +1131,7 @@ public class DatabaseHelper {
|
||||
*/
|
||||
public static HistoryEntry getOnlyHistoryEntryOfType(
|
||||
EppResource resource, final HistoryEntry.Type type) {
|
||||
List<HistoryEntry> historyEntries = getHistoryEntriesOfType(resource, type);
|
||||
List<HistoryEntry> historyEntries = getHistoryEntriesOfType(resource, type);
|
||||
assertThat(historyEntries).hasSize(1);
|
||||
return historyEntries.get(0);
|
||||
}
|
||||
@@ -1151,13 +1139,16 @@ public class DatabaseHelper {
|
||||
private static HistoryEntry.Type getHistoryEntryType(EppResource resource) {
|
||||
if (resource instanceof ContactResource) {
|
||||
return resource.getRepoId() != null
|
||||
? HistoryEntry.Type.CONTACT_CREATE : HistoryEntry.Type.CONTACT_UPDATE;
|
||||
? HistoryEntry.Type.CONTACT_CREATE
|
||||
: HistoryEntry.Type.CONTACT_UPDATE;
|
||||
} else if (resource instanceof HostResource) {
|
||||
return resource.getRepoId() != null
|
||||
? HistoryEntry.Type.HOST_CREATE : HistoryEntry.Type.HOST_UPDATE;
|
||||
? HistoryEntry.Type.HOST_CREATE
|
||||
: HistoryEntry.Type.HOST_UPDATE;
|
||||
} else if (resource instanceof DomainBase) {
|
||||
return resource.getRepoId() != null
|
||||
? HistoryEntry.Type.DOMAIN_CREATE : HistoryEntry.Type.DOMAIN_UPDATE;
|
||||
? HistoryEntry.Type.DOMAIN_CREATE
|
||||
: HistoryEntry.Type.DOMAIN_UPDATE;
|
||||
} else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
@@ -1215,7 +1206,7 @@ public class DatabaseHelper {
|
||||
tm().clearSessionCache();
|
||||
}
|
||||
|
||||
/** Force the create and update timestamps to get written into the resource. **/
|
||||
/** Force the create and update timestamps to get written into the resource. */
|
||||
public static <R> R cloneAndSetAutoTimestamps(final R resource) {
|
||||
R result;
|
||||
if (tm().isOfy()) {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import com.beust.jcommander.ParameterException;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
@DualDatabaseTest
|
||||
public class GetPremiumListCommandTest extends CommandTestCase<GetPremiumListCommand> {
|
||||
|
||||
private static final String BASE_LIST_CONTENTS =
|
||||
"tld:\n"
|
||||
+ "aluminum,USD 11.00\n"
|
||||
+ "brass,USD 20.00\n"
|
||||
+ "copper,USD 15.00\n"
|
||||
+ "diamond,USD 1000000.00\n"
|
||||
+ "gold,USD 24317.00\n"
|
||||
+ "iridium,USD 13117.00\n"
|
||||
+ "palladium,USD 877.00\n"
|
||||
+ "platinum,USD 87741.00\n"
|
||||
+ "rhodium,USD 88415.00\n"
|
||||
+ "rich,USD 100.00\n"
|
||||
+ "richer,USD 1000.00\n"
|
||||
+ "silver,USD 588.00\n";
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_list() throws Exception {
|
||||
runCommand("tld");
|
||||
assertStdoutIs(BASE_LIST_CONTENTS);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_onlyOneExists() throws Exception {
|
||||
runCommand("tld", "nonexistent");
|
||||
assertStdoutIs(BASE_LIST_CONTENTS + "No list found with name nonexistent.\n");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_nonexistent() throws Exception {
|
||||
runCommand("nonexistent", "othernonexistent");
|
||||
assertStdoutIs(
|
||||
"No list found with name nonexistent.\nNo list found with name othernonexistent.\n");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_noArgs() {
|
||||
assertThrows(ParameterException.class, this::runCommand);
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ public class WhoisActionTest {
|
||||
whoisAction.input = new StringReader(input);
|
||||
whoisAction.response = response;
|
||||
whoisAction.whoisReader =
|
||||
new WhoisReader(new WhoisCommandFactory(), "Please contact registrar");
|
||||
new WhoisReader(WhoisCommandFactory.createCached(), "Please contact registrar");
|
||||
whoisAction.whoisMetrics = new WhoisMetrics();
|
||||
whoisAction.metricBuilder = WhoisMetric.builderForRequest(clock);
|
||||
whoisAction.disclaimer =
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.whois;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.newDomainBase;
|
||||
import static google.registry.testing.DatabaseHelper.newHostResource;
|
||||
import static google.registry.testing.DatabaseHelper.newRegistry;
|
||||
import static google.registry.testing.DatabaseHelper.persistNewRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.TestCacheExtension;
|
||||
import java.net.InetAddress;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
class WhoisCommandFactoryTest {
|
||||
|
||||
FakeClock clock = new FakeClock();
|
||||
|
||||
@RegisterExtension
|
||||
final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withDatastoreAndCloudSql().withClock(clock).build();
|
||||
|
||||
@RegisterExtension
|
||||
final TestCacheExtension testCacheExtension =
|
||||
new TestCacheExtension.Builder().withEppResourceCache(Duration.millis(1000000000)).build();
|
||||
|
||||
WhoisCommandFactory noncachedFactory = WhoisCommandFactory.createNonCached();
|
||||
WhoisCommandFactory cachedFactory = WhoisCommandFactory.createCached();
|
||||
DomainBase domain;
|
||||
HostResource host;
|
||||
Registrar otherRegistrar;
|
||||
|
||||
int origSingletonCacheRefreshSeconds;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
persistResource(newRegistry("tld", "TLD"));
|
||||
host =
|
||||
newHostResource("ns.example.tld")
|
||||
.asBuilder()
|
||||
.setInetAddresses(ImmutableSet.of(InetAddress.getByName("1.2.3.4")))
|
||||
.build();
|
||||
persistResource(host);
|
||||
domain = newDomainBase("example.tld", host);
|
||||
persistResource(domain);
|
||||
otherRegistrar = persistNewRegistrar("OtherRegistrar");
|
||||
otherRegistrar =
|
||||
persistResource(
|
||||
otherRegistrar
|
||||
.asBuilder()
|
||||
.setState(Registrar.State.ACTIVE)
|
||||
.setPhoneNumber("+1.2223334444")
|
||||
.build());
|
||||
|
||||
// In addition to the TestCacheExtension, we have to set a long singleton cache timeout.
|
||||
RegistryConfig.CONFIG_SETTINGS.get().caching.singletonCacheRefreshSeconds = 1000000;
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
// Restore the singleton cache timeout. For some reason, this doesn't work if we store the
|
||||
// original value in an instance variable (I suspect there may be some overlap in test
|
||||
// execution) so just restore to zero.
|
||||
RegistryConfig.CONFIG_SETTINGS.get().caching.singletonCacheRefreshSeconds = 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNonCached_NameserverLookupByHostCommand() throws Exception {
|
||||
WhoisResponse response =
|
||||
noncachedFactory
|
||||
.nameserverLookupByHost(InternetDomainName.from("ns.example.tld"))
|
||||
.executeQuery(clock.nowUtc());
|
||||
assertThat(response.getResponse(false, "").plainTextOutput())
|
||||
.contains("Registrar: The Registrar");
|
||||
|
||||
// Note that we can't use persistResource() for these as that clears the cache.
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().put(
|
||||
host.asBuilder()
|
||||
.setPersistedCurrentSponsorClientId("OtherRegistrar")
|
||||
.build()));
|
||||
response =
|
||||
noncachedFactory
|
||||
.nameserverLookupByHost(InternetDomainName.from("ns.example.tld"))
|
||||
.executeQuery(clock.nowUtc());
|
||||
assertThat(response.getResponse(false, "").plainTextOutput())
|
||||
.contains("Registrar: OtherRegistrar name");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCached_NameserverLookupByHostCommand() throws Exception {
|
||||
WhoisResponse response =
|
||||
cachedFactory
|
||||
.nameserverLookupByHost(InternetDomainName.from("ns.example.tld"))
|
||||
.executeQuery(clock.nowUtc());
|
||||
assertThat(response.getResponse(false, "").plainTextOutput())
|
||||
.contains("Registrar: The Registrar");
|
||||
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().put(
|
||||
host.asBuilder()
|
||||
.setPersistedCurrentSponsorClientId("OtherRegistrar")
|
||||
.build()));
|
||||
response =
|
||||
cachedFactory
|
||||
.nameserverLookupByHost(InternetDomainName.from("ns.example.tld"))
|
||||
.executeQuery(clock.nowUtc());
|
||||
assertThat(response.getResponse(false, "").plainTextOutput())
|
||||
.contains("Registrar: The Registrar");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNonCached_DomainLookupCommand() throws Exception {
|
||||
WhoisResponse response =
|
||||
noncachedFactory
|
||||
.domainLookup(InternetDomainName.from("example.tld"), true, "REDACTED")
|
||||
.executeQuery(clock.nowUtc());
|
||||
assertThat(response.getResponse(false, "").plainTextOutput())
|
||||
.contains("Registrar: The Registrar");
|
||||
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().put(
|
||||
domain
|
||||
.asBuilder()
|
||||
.setPersistedCurrentSponsorClientId("OtherRegistrar")
|
||||
.build()));
|
||||
response =
|
||||
noncachedFactory
|
||||
.domainLookup(InternetDomainName.from("example.tld"), true, "REDACTED")
|
||||
.executeQuery(clock.nowUtc());
|
||||
assertThat(response.getResponse(false, "").plainTextOutput())
|
||||
.contains("Registrar: OtherRegistrar name");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCached_DomainLookupCommand() throws Exception {
|
||||
WhoisResponse response =
|
||||
cachedFactory
|
||||
.domainLookup(InternetDomainName.from("example.tld"), true, "REDACTED")
|
||||
.executeQuery(clock.nowUtc());
|
||||
assertThat(response.getResponse(false, "").plainTextOutput())
|
||||
.contains("Registrar: The Registrar");
|
||||
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().put(
|
||||
domain
|
||||
.asBuilder()
|
||||
.setPersistedCurrentSponsorClientId("OtherRegistrar")
|
||||
.build()));
|
||||
response =
|
||||
cachedFactory
|
||||
.domainLookup(InternetDomainName.from("example.tld"), true, "REDACTED")
|
||||
.executeQuery(clock.nowUtc());
|
||||
assertThat(response.getResponse(false, "").plainTextOutput())
|
||||
.contains("Registrar: The Registrar");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNonCached_RegistrarLookupCommand() throws Exception {
|
||||
WhoisResponse response =
|
||||
noncachedFactory.registrarLookup("OtherRegistrar").executeQuery(clock.nowUtc());
|
||||
assertThat(response.getResponse(false, "").plainTextOutput())
|
||||
.contains("Phone Number: +1.2223334444");
|
||||
|
||||
tm().transact(
|
||||
() -> tm().put(otherRegistrar.asBuilder().setPhoneNumber("+1.2345677890").build()));
|
||||
response = noncachedFactory.registrarLookup("OtherRegistrar").executeQuery(clock.nowUtc());
|
||||
assertThat(response.getResponse(false, "").plainTextOutput())
|
||||
.contains("Phone Number: +1.2345677890");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCached_RegistrarLookupCommand() throws Exception {
|
||||
WhoisResponse response =
|
||||
cachedFactory.registrarLookup("OtherRegistrar").executeQuery(clock.nowUtc());
|
||||
assertThat(response.getResponse(false, "").plainTextOutput())
|
||||
.contains("Phone Number: +1.2223334444");
|
||||
|
||||
tm().transact(
|
||||
() -> tm().put(otherRegistrar.asBuilder().setPhoneNumber("+1.2345677890").build()));
|
||||
response = cachedFactory.registrarLookup("OtherRegistrar").executeQuery(clock.nowUtc());
|
||||
assertThat(response.getResponse(false, "").plainTextOutput())
|
||||
.contains("Phone Number: +1.2223334444");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNonCached_NameserverLookupByIpCommand() throws Exception {
|
||||
// Note that this lookup currently doesn't cache the hosts, so there's no point in testing the
|
||||
// "cached" case. This test is here so that it will fail if anyone adds caching.
|
||||
WhoisResponse response =
|
||||
noncachedFactory
|
||||
.nameserverLookupByIp(InetAddress.getByName("1.2.3.4"))
|
||||
.executeQuery(clock.nowUtc());
|
||||
assertThat(response.getResponse(false, "").plainTextOutput())
|
||||
.contains("Registrar: The Registrar");
|
||||
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().put(
|
||||
host.asBuilder()
|
||||
.setPersistedCurrentSponsorClientId("OtherRegistrar")
|
||||
.build()));
|
||||
response =
|
||||
noncachedFactory
|
||||
.nameserverLookupByIp(InetAddress.getByName("1.2.3.4"))
|
||||
.executeQuery(clock.nowUtc());
|
||||
assertThat(response.getResponse(false, "").plainTextOutput())
|
||||
.contains("Registrar: OtherRegistrar");
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ class WhoisHttpActionTest {
|
||||
whoisAction.requestPath = WhoisHttpAction.PATH + pathInfo;
|
||||
whoisAction.response = response;
|
||||
whoisAction.whoisReader =
|
||||
new WhoisReader(new WhoisCommandFactory(), "Please contact registrar");
|
||||
new WhoisReader(WhoisCommandFactory.createCached(), "Please contact registrar");
|
||||
whoisAction.whoisMetrics = new WhoisMetrics();
|
||||
whoisAction.metricBuilder = WhoisMetric.builderForRequest(clock);
|
||||
whoisAction.disclaimer =
|
||||
|
||||
@@ -48,7 +48,7 @@ class WhoisReaderTest {
|
||||
@SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
|
||||
<T> T readCommand(String commandStr) throws Exception {
|
||||
return (T)
|
||||
new WhoisReader(new WhoisCommandFactory(), "Please contact registrar")
|
||||
new WhoisReader(WhoisCommandFactory.createCached(), "Please contact registrar")
|
||||
.readCommand(new StringReader(commandStr), false, clock.nowUtc());
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ HistoryEntry
|
||||
HostResource
|
||||
KmsSecret
|
||||
KmsSecretRevision
|
||||
LastSqlTransaction
|
||||
Modification
|
||||
OneTime
|
||||
PollMessage
|
||||
|
||||
+3
-3
@@ -28,7 +28,7 @@
|
||||
<fee:command name="create">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-03T10:00:00.000Z">50.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-03T10:00:00.002Z">50.00</fee:fee>
|
||||
<fee:date>2010-01-02T13:22:21Z</fee:date>
|
||||
<fee:notAfter>2010-01-03T10:00:00.000Z</fee:notAfter>
|
||||
</fee:command>
|
||||
@@ -40,7 +40,7 @@
|
||||
<fee:command name="create">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-03T10:00:00.000Z">50.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-03T10:00:00.002Z">50.00</fee:fee>
|
||||
<fee:date>2010-01-02T13:22:21Z</fee:date>
|
||||
<fee:notAfter>2010-01-03T10:00:00.000Z</fee:notAfter>
|
||||
</fee:command>
|
||||
@@ -52,7 +52,7 @@
|
||||
<fee:command name="create">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-03T10:00:00.000Z">50.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-03T10:00:00.002Z">50.00</fee:fee>
|
||||
<fee:date>2010-01-02T13:22:21Z</fee:date>
|
||||
<fee:notAfter>2010-01-03T10:00:00.000Z</fee:notAfter>
|
||||
</fee:command>
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00</fee:fee>
|
||||
</fee:cd>
|
||||
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||
<fee:name>example3.tld</fee:name>
|
||||
@@ -33,7 +33,7 @@
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">2</fee:period>
|
||||
<fee:fee description="create">26.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00</fee:fee>
|
||||
</fee:cd>
|
||||
</fee:chkData>
|
||||
</extension>
|
||||
|
||||
+3
-3
@@ -28,7 +28,7 @@
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00</fee:fee>
|
||||
</fee:cd>
|
||||
<fee:cd avail="1">
|
||||
<fee:object>
|
||||
@@ -38,7 +38,7 @@
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00</fee:fee>
|
||||
</fee:cd>
|
||||
<fee:cd avail="1">
|
||||
<fee:object>
|
||||
@@ -48,7 +48,7 @@
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00</fee:fee>
|
||||
</fee:cd>
|
||||
</fee:chkData>
|
||||
</extension>
|
||||
|
||||
+3
-3
@@ -28,7 +28,7 @@
|
||||
<fee:command name="create">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00</fee:fee>
|
||||
<fee:notAfter>2010-01-02T10:00:00.000Z</fee:notAfter>
|
||||
</fee:command>
|
||||
</fee:cd>
|
||||
@@ -39,7 +39,7 @@
|
||||
<fee:command name="create">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00</fee:fee>
|
||||
<fee:notAfter>2010-01-02T10:00:00.000Z</fee:notAfter>
|
||||
</fee:command>
|
||||
</fee:cd>
|
||||
@@ -50,7 +50,7 @@
|
||||
<fee:command name="create">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00</fee:fee>
|
||||
<fee:notAfter>2010-01-02T10:00:00.000Z</fee:notAfter>
|
||||
</fee:command>
|
||||
</fee:cd>
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">100.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00
|
||||
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00
|
||||
</fee:fee>
|
||||
<fee:class>premium</fee:class>
|
||||
</fee:cd>
|
||||
|
||||
@@ -916,3 +916,7 @@ class google.registry.persistence.DomainHistoryVKey {
|
||||
java.lang.Long historyRevisionId;
|
||||
java.lang.String repoId;
|
||||
}
|
||||
class google.registry.schema.replay.LastSqlTransaction {
|
||||
@Id long id;
|
||||
long transactionId;
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
mock-maker-inline
|
||||
+15
-8
@@ -20,8 +20,6 @@ plugins {
|
||||
ext {
|
||||
Set restrictedDbEnv =
|
||||
[ 'sandbox', 'production' ].asUnmodifiable()
|
||||
Set allDbEnv =
|
||||
[ 'alpha', 'crash' ].plus(restrictedDbEnv).asUnmodifiable()
|
||||
|
||||
def dbServerProperty = 'dbServer'
|
||||
def dbNameProperty = 'dbName'
|
||||
@@ -57,12 +55,12 @@ ext {
|
||||
}
|
||||
|
||||
getJdbcAccessInfo = {
|
||||
if (allDbEnv.contains(dbServer)) {
|
||||
if (rootProject.projects.keySet().contains(dbServer)) {
|
||||
return getSocketFactoryAccessInfo(dbServer)
|
||||
} else if (!dbServer.isEmpty()) {
|
||||
return getAccessInfoByHostPort(dbServer)
|
||||
} else {
|
||||
// Not connecting to a database. Return a dummy object for Flyway config.
|
||||
// Not running flyway tasks. Return a dummy object for Flyway config.
|
||||
return [ url: '', user: '', password: '' ]
|
||||
}
|
||||
}
|
||||
@@ -74,10 +72,8 @@ ext {
|
||||
// production. The role parameter may be superuser. (More roles will be added
|
||||
// later).
|
||||
getCloudSqlCredential = { env, role ->
|
||||
def devProject = project.hasProperty('devProject')
|
||||
? project.getProperty('devProject') : rootProject.devProject
|
||||
def gcpProject = project.hasProperty('gcpProject')
|
||||
? project.getProperty('gcpProject') : rootProject.gcpProject
|
||||
def devProject = rootProject.devProject
|
||||
def gcpProject = rootProject.projects[env]
|
||||
def keyProject = env in restrictedDbEnv? devProject : gcpProject
|
||||
def command =
|
||||
"""gsutil cp \
|
||||
@@ -137,6 +133,17 @@ publishing {
|
||||
}
|
||||
}
|
||||
|
||||
// Adds flyway tasks such as: flywayInfo, flywayValidate, flywayMigrate (
|
||||
// deploying the schema in local repository), and flywayClean (dropping all data
|
||||
// in the database). The latter two commands are disallowed in environments
|
||||
// listed in ext.restrictedDbEnv.
|
||||
//
|
||||
// Examples:
|
||||
// Get info in alpha: nom_build :db:flywayInfo --dbServer=alpha
|
||||
// Deploy schema to a local test instance and override the database name:
|
||||
// nom_build :db:flywayMigrate --dbServer=localhost:5432 --dbName=not-default \
|
||||
// --dbUser=... --dbPassword=...
|
||||
|
||||
flyway {
|
||||
def accessInfo = project.ext.getJdbcAccessInfo()
|
||||
|
||||
|
||||
+3
-2
@@ -73,6 +73,7 @@ ext {
|
||||
'com.google.http-client:google-http-client-appengine:1.34.1',
|
||||
'com.google.http-client:google-http-client-jackson2:1.34.1',
|
||||
'com.google.http-client:google-http-client:1.34.1',
|
||||
'com.google.inject:guice:5.0.1',
|
||||
'com.google.javascript:closure-compiler:v20190301',
|
||||
'com.google.monitoring-client:contrib:1.0.7',
|
||||
'com.google.monitoring-client:metrics:1.0.7',
|
||||
@@ -157,8 +158,8 @@ ext {
|
||||
'org.joda:joda-money:1.0.1',
|
||||
'org.json:json:20160810',
|
||||
'org.jsoup:jsoup:1.13.1',
|
||||
'org.mockito:mockito-core:3.3.3',
|
||||
'org.mockito:mockito-junit-jupiter:3.3.3',
|
||||
'org.mockito:mockito-core:3.7.7',
|
||||
'org.mockito:mockito-junit-jupiter:3.7.7',
|
||||
'org.mortbay.jetty:jetty:6.1.26',
|
||||
'org.postgresql:postgresql:42.2.18',
|
||||
'org.seleniumhq.selenium:selenium-api:3.141.59',
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -28,6 +28,7 @@ dependencies {
|
||||
compile deps['org.bouncycastle:bcpkix-jdk15on']
|
||||
compile deps['org.bouncycastle:bcprov-jdk15on']
|
||||
compile project(':util')
|
||||
compile project(':common')
|
||||
|
||||
runtime deps['com.google.flogger:flogger-system-backend']
|
||||
runtime deps['io.netty:netty-tcnative-boringssl-static']
|
||||
@@ -38,9 +39,11 @@ dependencies {
|
||||
testCompile deps['org.junit.jupiter:junit-jupiter-params']
|
||||
testCompile deps['org.bouncycastle:bcpkix-jdk15on']
|
||||
testCompile deps['org.bouncycastle:bcprov-jdk15on']
|
||||
testCompile project(path: ':common', configuration: 'testing')
|
||||
|
||||
annotationProcessor deps['com.google.dagger:dagger-compiler']
|
||||
testAnnotationProcessor deps['com.google.dagger:dagger-compiler']
|
||||
compile 'joda-time:joda-time:2.9.2'
|
||||
}
|
||||
|
||||
// Make testing artifacts available to be depended up on by other projects.
|
||||
|
||||
+49
-4
@@ -19,6 +19,7 @@ import static google.registry.util.X509Utils.getCertificateHash;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.util.Clock;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
@@ -29,6 +30,7 @@ import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
import io.netty.handler.ssl.SslProvider;
|
||||
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
import io.netty.util.AttributeKey;
|
||||
import io.netty.util.concurrent.Future;
|
||||
@@ -41,6 +43,7 @@ import java.security.cert.X509Certificate;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.function.Supplier;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Adds a server side SSL handler to the channel pipeline.
|
||||
@@ -66,6 +69,29 @@ public class SslServerInitializer<C extends Channel> extends ChannelInitializer<
|
||||
public static final AttributeKey<Promise<X509Certificate>> CLIENT_CERTIFICATE_PROMISE_KEY =
|
||||
AttributeKey.valueOf("CLIENT_CERTIFICATE_PROMISE_KEY");
|
||||
|
||||
/**
|
||||
* The list of cipher suites that are currently acceptable to create a successful handshake.
|
||||
*
|
||||
* <p>This list includes all of the current TLS1.3 ciphers and a collection of TLS1.2 ciphers with
|
||||
* no known security vulnerabilities. Note that OpenSSL uses a separate nomenclature for the
|
||||
* ciphers internally but the IANA names listed here will be transparently translated by the
|
||||
* OpenSSL provider (if used), so there is no need to include the OpenSSL name variants here. More
|
||||
* information about these cipher suites and their OpenSSL names can be found at ciphersuite.info.
|
||||
*/
|
||||
private static final ImmutableList ALLOWED_TLS_CIPHERS =
|
||||
ImmutableList.of(
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_AES_128_GCM_SHA256",
|
||||
"TLS_AES_256_GCM_SHA384",
|
||||
"TLS_CHACHA20_POLY1305_SHA256",
|
||||
"TLS_AES_128_CCM_SHA256",
|
||||
"TLS_AES_128_CCM_8_SHA256");
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private final boolean requireClientCert;
|
||||
// TODO(jianglai): Always validate client certs (if required).
|
||||
@@ -76,13 +102,19 @@ public class SslServerInitializer<C extends Channel> extends ChannelInitializer<
|
||||
private final Supplier<PrivateKey> privateKeySupplier;
|
||||
private final Supplier<ImmutableList<X509Certificate>> certificatesSupplier;
|
||||
private final ImmutableList<String> supportedSslVersions;
|
||||
// TODO(sarahbot): Remove this variable and its check after enforcement start date has passed.
|
||||
private final ImmutableList<String> oldSupportedSslVersions;
|
||||
private final DateTime enforcementStartTime;
|
||||
private final Clock clock;
|
||||
|
||||
public SslServerInitializer(
|
||||
boolean requireClientCert,
|
||||
boolean validateClientCert,
|
||||
SslProvider sslProvider,
|
||||
Supplier<PrivateKey> privateKeySupplier,
|
||||
Supplier<ImmutableList<X509Certificate>> certificatesSupplier) {
|
||||
Supplier<ImmutableList<X509Certificate>> certificatesSupplier,
|
||||
DateTime enforcementStartTime,
|
||||
Clock clock) {
|
||||
logger.atInfo().log("Server SSL Provider: %s", sslProvider);
|
||||
checkArgument(
|
||||
requireClientCert || !validateClientCert,
|
||||
@@ -94,10 +126,16 @@ public class SslServerInitializer<C extends Channel> extends ChannelInitializer<
|
||||
this.certificatesSupplier = certificatesSupplier;
|
||||
this.supportedSslVersions =
|
||||
sslProvider == SslProvider.OPENSSL
|
||||
? ImmutableList.of("TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1")
|
||||
// JDK support for TLS 1.3 won't be available until 2020-07-14 at the earliest.
|
||||
? ImmutableList.of("TLSv1.3", "TLSv1.2")
|
||||
// JDK support for TLS 1.3 won't be available until 2021-04-20 at the earliest.
|
||||
// See: https://java.com/en/jre-jdk-cryptoroadmap.html
|
||||
: ImmutableList.of("TLSv1.2");
|
||||
this.oldSupportedSslVersions =
|
||||
sslProvider == SslProvider.OPENSSL
|
||||
? ImmutableList.of("TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1")
|
||||
: ImmutableList.of("TLSv1.2", "TLSv1.1", "TLSv1");
|
||||
this.enforcementStartTime = enforcementStartTime;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,8 +147,15 @@ public class SslServerInitializer<C extends Channel> extends ChannelInitializer<
|
||||
.sslProvider(sslProvider)
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.clientAuth(requireClientCert ? ClientAuth.REQUIRE : ClientAuth.NONE)
|
||||
.protocols(supportedSslVersions)
|
||||
.protocols(
|
||||
enforcementStartTime.isBefore(clock.nowUtc())
|
||||
? supportedSslVersions
|
||||
: oldSupportedSslVersions)
|
||||
.ciphers(
|
||||
enforcementStartTime.isBefore(clock.nowUtc()) ? ALLOWED_TLS_CIPHERS : null,
|
||||
SupportedCipherSuiteFilter.INSTANCE)
|
||||
.build();
|
||||
|
||||
logger.atInfo().log("Available Cipher Suites: %s", sslContext.cipherSuites());
|
||||
SslHandler sslHandler = sslContext.newHandler(channel.alloc());
|
||||
if (requireClientCert) {
|
||||
|
||||
+196
-4
@@ -23,6 +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.testing.FakeClock;
|
||||
import google.registry.util.SelfSignedCaCertificate;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
@@ -42,13 +43,16 @@ import java.security.cert.CertificateNotYetValidException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
@@ -97,19 +101,29 @@ class SslServerInitializerTest {
|
||||
validateClientCert,
|
||||
sslProvider,
|
||||
Suppliers.ofInstance(privateKey),
|
||||
Suppliers.ofInstance(ImmutableList.copyOf(certificates)));
|
||||
Suppliers.ofInstance(ImmutableList.copyOf(certificates)),
|
||||
DateTime.parse("2021-04-01T16:00:00Z"),
|
||||
new FakeClock(DateTime.parse("2021-05-01T16:00:00Z")));
|
||||
}
|
||||
|
||||
private ChannelHandler getClientHandler(
|
||||
SslProvider sslProvider,
|
||||
X509Certificate trustedCertificate,
|
||||
PrivateKey privateKey,
|
||||
X509Certificate certificate) {
|
||||
X509Certificate certificate,
|
||||
String protocol,
|
||||
List<String> cipher) {
|
||||
return new ChannelInitializer<LocalChannel>() {
|
||||
@Override
|
||||
protected void initChannel(LocalChannel ch) throws Exception {
|
||||
SslContextBuilder sslContextBuilder =
|
||||
SslContextBuilder.forClient().trustManager(trustedCertificate).sslProvider(sslProvider);
|
||||
SslContextBuilder.forClient()
|
||||
.trustManager(trustedCertificate)
|
||||
.sslProvider(sslProvider)
|
||||
.ciphers(cipher);
|
||||
if (protocol != null) {
|
||||
sslContextBuilder.protocols(protocol);
|
||||
}
|
||||
if (privateKey != null && certificate != null) {
|
||||
sslContextBuilder.keyManager(privateKey, certificate);
|
||||
}
|
||||
@@ -127,6 +141,14 @@ class SslServerInitializerTest {
|
||||
};
|
||||
}
|
||||
|
||||
private ChannelHandler getClientHandler(
|
||||
SslProvider sslProvider,
|
||||
X509Certificate trustedCertificate,
|
||||
PrivateKey privateKey,
|
||||
X509Certificate certificate) {
|
||||
return getClientHandler(sslProvider, trustedCertificate, privateKey, certificate, null, null);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testSuccess_swappedInitializerWithSslHandler(SslProvider sslProvider) throws Exception {
|
||||
@@ -137,7 +159,9 @@ class SslServerInitializerTest {
|
||||
false,
|
||||
sslProvider,
|
||||
Suppliers.ofInstance(ssc.key()),
|
||||
Suppliers.ofInstance(ImmutableList.of(ssc.cert())));
|
||||
Suppliers.ofInstance(ImmutableList.of(ssc.cert())),
|
||||
DateTime.parse("2021-04-01T16:00:00Z"),
|
||||
new FakeClock(DateTime.parse("2021-05-01T16:00:00Z")));
|
||||
EmbeddedChannel channel = new EmbeddedChannel();
|
||||
ChannelPipeline pipeline = channel.pipeline();
|
||||
pipeline.addLast(sslServerInitializer);
|
||||
@@ -172,6 +196,174 @@ class SslServerInitializerTest {
|
||||
assertThat(sslSession.getPeerCertificates()).asList().containsExactly(serverSsc.cert());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testFailure_cipherNotAccepted(SslProvider sslProvider) throws Exception {
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
LocalAddress localAddress = new LocalAddress("CIPHER_NOT_ACCEPTED_" + sslProvider);
|
||||
|
||||
nettyExtension.setUpServer(
|
||||
localAddress, getServerHandler(true, true, sslProvider, serverSsc.key(), serverSsc.cert()));
|
||||
SelfSignedCaCertificate clientSsc =
|
||||
SelfSignedCaCertificate.create(
|
||||
"CLIENT",
|
||||
Date.from(Instant.now().minus(Duration.ofDays(2))),
|
||||
Date.from(Instant.now().plus(Duration.ofDays(1))));
|
||||
nettyExtension.setUpClient(
|
||||
localAddress,
|
||||
getClientHandler(
|
||||
sslProvider,
|
||||
serverSsc.cert(),
|
||||
clientSsc.key(),
|
||||
clientSsc.cert(),
|
||||
"TLSv1.2",
|
||||
Collections.singletonList("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA")));
|
||||
|
||||
verifySslException(
|
||||
nettyExtension.getServerChannel(),
|
||||
channel -> channel.attr(CLIENT_CERTIFICATE_PROMISE_KEY).get().get(),
|
||||
SSLHandshakeException.class);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testSuccess_someCiphersNotAccepted(SslProvider sslProvider) throws Exception {
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
LocalAddress localAddress = new LocalAddress("SOME_CIPHERS_NOT_ACCEPTED_" + sslProvider);
|
||||
|
||||
nettyExtension.setUpServer(
|
||||
localAddress,
|
||||
new SslServerInitializer<LocalChannel>(
|
||||
true,
|
||||
true,
|
||||
sslProvider,
|
||||
Suppliers.ofInstance(serverSsc.key()),
|
||||
Suppliers.ofInstance(ImmutableList.of(serverSsc.cert())),
|
||||
DateTime.parse("2021-04-01T16:00:00Z"),
|
||||
new FakeClock(DateTime.parse("2021-05-01T16:00:00Z"))));
|
||||
SelfSignedCaCertificate clientSsc =
|
||||
SelfSignedCaCertificate.create(
|
||||
"CLIENT",
|
||||
Date.from(Instant.now().minus(Duration.ofDays(2))),
|
||||
Date.from(Instant.now().plus(Duration.ofDays(1))));
|
||||
nettyExtension.setUpClient(
|
||||
localAddress,
|
||||
getClientHandler(
|
||||
sslProvider,
|
||||
serverSsc.cert(),
|
||||
clientSsc.key(),
|
||||
clientSsc.cert(),
|
||||
"TLSv1.2",
|
||||
ImmutableList.of(
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", // Only accepted cipher
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA")));
|
||||
|
||||
SSLSession sslSession = setUpSslChannel(nettyExtension.getClientChannel(), serverSsc.cert());
|
||||
nettyExtension.assertThatMessagesWork();
|
||||
|
||||
assertThat(sslSession.getLocalCertificates()).asList().containsExactly(clientSsc.cert());
|
||||
assertThat(sslSession.getPeerCertificates()).asList().containsExactly(serverSsc.cert());
|
||||
assertThat(sslSession.getCipherSuite()).isEqualTo("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testSuccess_cipherNotAccepted_beforeEnforcementDate(SslProvider sslProvider)
|
||||
throws Exception {
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
LocalAddress localAddress = new LocalAddress("CIPHER_ACCEPTED_BEFORE_DATE_" + sslProvider);
|
||||
|
||||
nettyExtension.setUpServer(
|
||||
localAddress,
|
||||
new SslServerInitializer<LocalChannel>(
|
||||
true,
|
||||
true,
|
||||
sslProvider,
|
||||
Suppliers.ofInstance(serverSsc.key()),
|
||||
Suppliers.ofInstance(ImmutableList.of(serverSsc.cert())),
|
||||
DateTime.parse("2021-04-01T16:00:00Z"),
|
||||
new FakeClock(DateTime.parse("2021-03-01T16:00:00Z"))));
|
||||
SelfSignedCaCertificate clientSsc =
|
||||
SelfSignedCaCertificate.create(
|
||||
"CLIENT",
|
||||
Date.from(Instant.now().minus(Duration.ofDays(2))),
|
||||
Date.from(Instant.now().plus(Duration.ofDays(1))));
|
||||
nettyExtension.setUpClient(
|
||||
localAddress,
|
||||
getClientHandler(
|
||||
sslProvider,
|
||||
serverSsc.cert(),
|
||||
clientSsc.key(),
|
||||
clientSsc.cert(),
|
||||
"TLSv1.2",
|
||||
Collections.singletonList("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA")));
|
||||
|
||||
SSLSession sslSession = setUpSslChannel(nettyExtension.getClientChannel(), serverSsc.cert());
|
||||
nettyExtension.assertThatMessagesWork();
|
||||
|
||||
assertThat(sslSession.getLocalCertificates()).asList().containsExactly(clientSsc.cert());
|
||||
assertThat(sslSession.getPeerCertificates()).asList().containsExactly(serverSsc.cert());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testFailure_protocolNotAccepted(SslProvider sslProvider) throws Exception {
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
LocalAddress localAddress = new LocalAddress("PROTOCOL_NOT_ACCEPTED_" + sslProvider);
|
||||
|
||||
nettyExtension.setUpServer(
|
||||
localAddress, getServerHandler(true, true, sslProvider, serverSsc.key(), serverSsc.cert()));
|
||||
SelfSignedCaCertificate clientSsc =
|
||||
SelfSignedCaCertificate.create(
|
||||
"CLIENT",
|
||||
Date.from(Instant.now().minus(Duration.ofDays(2))),
|
||||
Date.from(Instant.now().plus(Duration.ofDays(1))));
|
||||
nettyExtension.setUpClient(
|
||||
localAddress,
|
||||
getClientHandler(
|
||||
sslProvider, serverSsc.cert(), clientSsc.key(), clientSsc.cert(), "TLSv1.1", null));
|
||||
|
||||
verifySslException(
|
||||
nettyExtension.getServerChannel(),
|
||||
channel -> channel.attr(CLIENT_CERTIFICATE_PROMISE_KEY).get().get(),
|
||||
SSLHandshakeException.class);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testSuccess_protocolNotAccepted_beforeEnforcementDate(SslProvider sslProvider)
|
||||
throws Exception {
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
LocalAddress localAddress = new LocalAddress("PROTOCOL_ACCEPTED_BEFORE_DATE_" + sslProvider);
|
||||
|
||||
nettyExtension.setUpServer(
|
||||
localAddress,
|
||||
new SslServerInitializer<LocalChannel>(
|
||||
true,
|
||||
true,
|
||||
sslProvider,
|
||||
Suppliers.ofInstance(serverSsc.key()),
|
||||
Suppliers.ofInstance(ImmutableList.of(serverSsc.cert())),
|
||||
DateTime.parse("2021-04-01T16:00:00Z"),
|
||||
new FakeClock(DateTime.parse("2021-03-01T16:00:00Z"))));
|
||||
SelfSignedCaCertificate clientSsc =
|
||||
SelfSignedCaCertificate.create(
|
||||
"CLIENT",
|
||||
Date.from(Instant.now().minus(Duration.ofDays(2))),
|
||||
Date.from(Instant.now().plus(Duration.ofDays(1))));
|
||||
nettyExtension.setUpClient(
|
||||
localAddress,
|
||||
getClientHandler(
|
||||
sslProvider, serverSsc.cert(), clientSsc.key(), clientSsc.cert(), "TLSv1.1", null));
|
||||
|
||||
SSLSession sslSession = setUpSslChannel(nettyExtension.getClientChannel(), serverSsc.cert());
|
||||
nettyExtension.assertThatMessagesWork();
|
||||
|
||||
assertThat(sslSession.getLocalCertificates()).asList().containsExactly(clientSsc.cert());
|
||||
assertThat(sslSession.getPeerCertificates()).asList().containsExactly(serverSsc.cert());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testFailure_clientCertExpired(SslProvider sslProvider) throws Exception {
|
||||
|
||||
@@ -48,8 +48,8 @@ javax.mail:mail:1.4
|
||||
javax.xml.bind:jaxb-api:2.3.0
|
||||
joda-time:joda-time:2.9.2
|
||||
junit:junit:4.12
|
||||
net.bytebuddy:byte-buddy-agent:1.10.5
|
||||
net.bytebuddy:byte-buddy:1.10.5
|
||||
net.bytebuddy:byte-buddy-agent:1.10.19
|
||||
net.bytebuddy:byte-buddy:1.10.19
|
||||
org.apache.httpcomponents:httpclient:4.5.11
|
||||
org.apache.httpcomponents:httpcore:4.4.13
|
||||
org.apiguardian:apiguardian-api:1.1.0
|
||||
@@ -64,8 +64,8 @@ org.junit.jupiter:junit-jupiter-params:5.6.2
|
||||
org.junit.platform:junit-platform-commons:1.6.2
|
||||
org.junit.platform:junit-platform-engine:1.6.2
|
||||
org.junit:junit-bom:5.6.2
|
||||
org.mockito:mockito-core:3.3.3
|
||||
org.objenesis:objenesis:2.6
|
||||
org.mockito:mockito-core:3.7.7
|
||||
org.objenesis:objenesis:3.1
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
org.yaml:snakeyaml:1.17
|
||||
xerces:xmlParserAPIs:2.6.2
|
||||
|
||||
@@ -48,8 +48,8 @@ javax.mail:mail:1.4
|
||||
javax.xml.bind:jaxb-api:2.3.0
|
||||
joda-time:joda-time:2.9.2
|
||||
junit:junit:4.12
|
||||
net.bytebuddy:byte-buddy-agent:1.10.5
|
||||
net.bytebuddy:byte-buddy:1.10.5
|
||||
net.bytebuddy:byte-buddy-agent:1.10.19
|
||||
net.bytebuddy:byte-buddy:1.10.19
|
||||
org.apache.httpcomponents:httpclient:4.5.11
|
||||
org.apache.httpcomponents:httpcore:4.4.13
|
||||
org.apiguardian:apiguardian-api:1.1.0
|
||||
@@ -64,8 +64,8 @@ org.junit.jupiter:junit-jupiter-params:5.6.2
|
||||
org.junit.platform:junit-platform-commons:1.6.2
|
||||
org.junit.platform:junit-platform-engine:1.6.2
|
||||
org.junit:junit-bom:5.6.2
|
||||
org.mockito:mockito-core:3.3.3
|
||||
org.objenesis:objenesis:2.6
|
||||
org.mockito:mockito-core:3.7.7
|
||||
org.objenesis:objenesis:3.1
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
org.yaml:snakeyaml:1.17
|
||||
xerces:xmlParserAPIs:2.6.2
|
||||
|
||||
@@ -48,8 +48,8 @@ javax.mail:mail:1.4
|
||||
javax.xml.bind:jaxb-api:2.3.0
|
||||
joda-time:joda-time:2.9.2
|
||||
junit:junit:4.12
|
||||
net.bytebuddy:byte-buddy-agent:1.10.5
|
||||
net.bytebuddy:byte-buddy:1.10.5
|
||||
net.bytebuddy:byte-buddy-agent:1.10.19
|
||||
net.bytebuddy:byte-buddy:1.10.19
|
||||
org.apache.httpcomponents:httpclient:4.5.11
|
||||
org.apache.httpcomponents:httpcore:4.4.13
|
||||
org.apiguardian:apiguardian-api:1.1.0
|
||||
@@ -64,8 +64,8 @@ org.junit.jupiter:junit-jupiter-params:5.6.2
|
||||
org.junit.platform:junit-platform-commons:1.6.2
|
||||
org.junit.platform:junit-platform-engine:1.6.2
|
||||
org.junit:junit-bom:5.6.2
|
||||
org.mockito:mockito-core:3.3.3
|
||||
org.objenesis:objenesis:2.6
|
||||
org.mockito:mockito-core:3.7.7
|
||||
org.objenesis:objenesis:3.1
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
org.yaml:snakeyaml:1.17
|
||||
xerces:xmlParserAPIs:2.6.2
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user