mirror of
https://github.com/google/nomulus
synced 2026-01-29 00:52:22 +00:00
Compare commits
15 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
073d0a416a | ||
|
|
f2ead5a0e3 | ||
|
|
212dbbe520 | ||
|
|
8594a61fd4 | ||
|
|
36837eb3e6 | ||
|
|
3a9a8c6557 | ||
|
|
65c2570b8f | ||
|
|
86acaa1b31 | ||
|
|
436cc03be9 | ||
|
|
e110ddd412 | ||
|
|
214b23e99c | ||
|
|
743dea9ca2 | ||
|
|
41f9f1ef7d | ||
|
|
44ede2b022 | ||
|
|
e4312322dc |
@@ -331,6 +331,13 @@ subprojects {
|
||||
|
||||
apply from: "${rootDir.path}/java_common.gradle"
|
||||
|
||||
if (project.name != 'docs') {
|
||||
compileJava {
|
||||
// TODO: Remove this once we migrate off AppEngine.
|
||||
options.release = 8
|
||||
}
|
||||
}
|
||||
|
||||
if (project.name == 'third_party') return
|
||||
|
||||
project.tasks.test.dependsOn runPresubmits
|
||||
|
||||
@@ -172,6 +172,7 @@ dependencies {
|
||||
testRuntime files(sourceSets.test.resources.srcDirs)
|
||||
|
||||
compile deps['com.beust:jcommander']
|
||||
compile deps['com.github.ben-manes.caffeine:caffeine']
|
||||
compile deps['com.google.api:gax']
|
||||
compile deps['com.google.api.grpc:proto-google-cloud-datastore-v1']
|
||||
compile deps['com.google.api.grpc:proto-google-common-protos']
|
||||
@@ -799,6 +800,11 @@ if (environment == 'alpha') {
|
||||
mainClass: 'google.registry.beam.comparedb.ValidateDatabasePipeline',
|
||||
metaData: 'google/registry/beam/validate_database_pipeline_metadata.json'
|
||||
],
|
||||
resaveAllEppResources:
|
||||
[
|
||||
mainClass: 'google.registry.beam.resave.ResaveAllEppResourcesPipeline',
|
||||
metaData: 'google/registry/beam/resave_all_epp_resources_pipeline_metadata.json'
|
||||
],
|
||||
]
|
||||
project.tasks.create("stageBeamPipelines") {
|
||||
doLast {
|
||||
|
||||
@@ -13,6 +13,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.6
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
@@ -118,7 +119,7 @@ com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.code.gson:gson:2.8.9
|
||||
com.google.common.html.types:types:1.0.6
|
||||
com.google.dagger:dagger:2.33
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.errorprone:error_prone_annotations:2.11.0
|
||||
com.google.escapevelocity:escapevelocity:0.9.1
|
||||
com.google.flatbuffers:flatbuffers-java:1.12.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
@@ -243,7 +244,7 @@ org.bouncycastle:bcpg-jdk15on:1.67
|
||||
org.bouncycastle:bcpkix-jdk15on:1.67
|
||||
org.bouncycastle:bcprov-jdk15on:1.67
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:3.21.0
|
||||
org.checkerframework:checker-qual:3.21.3
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.20
|
||||
|
||||
@@ -13,6 +13,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.6
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
|
||||
@@ -17,6 +17,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.6
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
@@ -123,7 +124,7 @@ com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.code.gson:gson:2.8.9
|
||||
com.google.common.html.types:types:1.0.6
|
||||
com.google.dagger:dagger:2.33
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.errorprone:error_prone_annotations:2.11.0
|
||||
com.google.escapevelocity:escapevelocity:0.9.1
|
||||
com.google.flatbuffers:flatbuffers-java:1.12.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
@@ -253,7 +254,7 @@ org.bouncycastle:bcpg-jdk15on:1.67
|
||||
org.bouncycastle:bcpkix-jdk15on:1.67
|
||||
org.bouncycastle:bcprov-jdk15on:1.67
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:3.21.0
|
||||
org.checkerframework:checker-qual:3.21.3
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.20
|
||||
|
||||
@@ -17,6 +17,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.6
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
@@ -123,7 +124,7 @@ com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.code.gson:gson:2.8.9
|
||||
com.google.common.html.types:types:1.0.6
|
||||
com.google.dagger:dagger:2.33
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.errorprone:error_prone_annotations:2.11.0
|
||||
com.google.escapevelocity:escapevelocity:0.9.1
|
||||
com.google.flatbuffers:flatbuffers-java:1.12.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
@@ -252,7 +253,7 @@ org.bouncycastle:bcpg-jdk15on:1.67
|
||||
org.bouncycastle:bcpkix-jdk15on:1.67
|
||||
org.bouncycastle:bcprov-jdk15on:1.67
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:3.21.0
|
||||
org.checkerframework:checker-qual:3.21.3
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.20
|
||||
|
||||
@@ -13,6 +13,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.6
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
@@ -118,7 +119,7 @@ com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.code.gson:gson:2.8.9
|
||||
com.google.common.html.types:types:1.0.6
|
||||
com.google.dagger:dagger:2.33
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.errorprone:error_prone_annotations:2.11.0
|
||||
com.google.escapevelocity:escapevelocity:0.9.1
|
||||
com.google.flatbuffers:flatbuffers-java:1.12.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
@@ -243,7 +244,7 @@ org.bouncycastle:bcpg-jdk15on:1.67
|
||||
org.bouncycastle:bcpkix-jdk15on:1.67
|
||||
org.bouncycastle:bcprov-jdk15on:1.67
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:3.21.0
|
||||
org.checkerframework:checker-qual:3.21.3
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.20
|
||||
|
||||
@@ -13,6 +13,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.6
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
@@ -117,7 +118,7 @@ com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.code.gson:gson:2.8.9
|
||||
com.google.common.html.types:types:1.0.6
|
||||
com.google.dagger:dagger:2.33
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.errorprone:error_prone_annotations:2.11.0
|
||||
com.google.escapevelocity:escapevelocity:0.9.1
|
||||
com.google.flatbuffers:flatbuffers-java:1.12.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
@@ -236,7 +237,7 @@ org.bouncycastle:bcpg-jdk15on:1.67
|
||||
org.bouncycastle:bcpkix-jdk15on:1.67
|
||||
org.bouncycastle:bcprov-jdk15on:1.67
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:3.21.0
|
||||
org.checkerframework:checker-qual:3.21.3
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.conscrypt:conscrypt-openjdk-uber:2.5.1
|
||||
|
||||
@@ -17,6 +17,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.6
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
@@ -122,7 +123,7 @@ com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.code.gson:gson:2.8.9
|
||||
com.google.common.html.types:types:1.0.6
|
||||
com.google.dagger:dagger:2.33
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.errorprone:error_prone_annotations:2.11.0
|
||||
com.google.escapevelocity:escapevelocity:0.9.1
|
||||
com.google.flatbuffers:flatbuffers-java:1.12.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
@@ -252,7 +253,7 @@ org.bouncycastle:bcpg-jdk15on:1.67
|
||||
org.bouncycastle:bcpkix-jdk15on:1.67
|
||||
org.bouncycastle:bcprov-jdk15on:1.67
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:3.21.0
|
||||
org.checkerframework:checker-qual:3.21.3
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.20
|
||||
|
||||
@@ -17,6 +17,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.6
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
@@ -122,7 +123,7 @@ com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.code.gson:gson:2.8.9
|
||||
com.google.common.html.types:types:1.0.6
|
||||
com.google.dagger:dagger:2.33
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.errorprone:error_prone_annotations:2.11.0
|
||||
com.google.escapevelocity:escapevelocity:0.9.1
|
||||
com.google.flatbuffers:flatbuffers-java:1.12.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
@@ -252,7 +253,7 @@ org.bouncycastle:bcpg-jdk15on:1.67
|
||||
org.bouncycastle:bcpkix-jdk15on:1.67
|
||||
org.bouncycastle:bcprov-jdk15on:1.67
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:3.21.0
|
||||
org.checkerframework:checker-qual:3.21.3
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.20
|
||||
|
||||
@@ -17,6 +17,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.6
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
@@ -122,7 +123,7 @@ com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.code.gson:gson:2.8.9
|
||||
com.google.common.html.types:types:1.0.6
|
||||
com.google.dagger:dagger:2.33
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.errorprone:error_prone_annotations:2.11.0
|
||||
com.google.escapevelocity:escapevelocity:0.9.1
|
||||
com.google.flatbuffers:flatbuffers-java:1.12.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
@@ -252,7 +253,7 @@ org.bouncycastle:bcpg-jdk15on:1.67
|
||||
org.bouncycastle:bcpkix-jdk15on:1.67
|
||||
org.bouncycastle:bcprov-jdk15on:1.67
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:3.21.0
|
||||
org.checkerframework:checker-qual:3.21.3
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.20
|
||||
|
||||
@@ -17,6 +17,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.6
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
|
||||
@@ -13,6 +13,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.6
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
@@ -120,7 +121,7 @@ com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.code.gson:gson:2.8.9
|
||||
com.google.common.html.types:types:1.0.6
|
||||
com.google.dagger:dagger:2.33
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.errorprone:error_prone_annotations:2.11.0
|
||||
com.google.escapevelocity:escapevelocity:0.9.1
|
||||
com.google.flatbuffers:flatbuffers-java:1.12.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
@@ -264,7 +265,7 @@ org.bouncycastle:bcpg-jdk15on:1.67
|
||||
org.bouncycastle:bcpkix-jdk15on:1.67
|
||||
org.bouncycastle:bcprov-jdk15on:1.67
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:3.21.0
|
||||
org.checkerframework:checker-qual:3.21.3
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.20
|
||||
|
||||
@@ -13,6 +13,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.6
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
@@ -119,7 +120,7 @@ com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.code.gson:gson:2.8.9
|
||||
com.google.common.html.types:types:1.0.6
|
||||
com.google.dagger:dagger:2.33
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.errorprone:error_prone_annotations:2.11.0
|
||||
com.google.escapevelocity:escapevelocity:0.9.1
|
||||
com.google.flatbuffers:flatbuffers-java:1.12.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
@@ -257,7 +258,7 @@ org.bouncycastle:bcpg-jdk15on:1.67
|
||||
org.bouncycastle:bcpkix-jdk15on:1.67
|
||||
org.bouncycastle:bcprov-jdk15on:1.67
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:3.21.0
|
||||
org.checkerframework:checker-qual:3.21.3
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.conscrypt:conscrypt-openjdk-uber:2.5.1
|
||||
|
||||
@@ -17,6 +17,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.6
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
@@ -125,7 +126,7 @@ com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.code.gson:gson:2.8.9
|
||||
com.google.common.html.types:types:1.0.6
|
||||
com.google.dagger:dagger:2.33
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.errorprone:error_prone_annotations:2.11.0
|
||||
com.google.escapevelocity:escapevelocity:0.9.1
|
||||
com.google.flatbuffers:flatbuffers-java:1.12.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
@@ -274,7 +275,7 @@ org.bouncycastle:bcpg-jdk15on:1.67
|
||||
org.bouncycastle:bcpkix-jdk15on:1.67
|
||||
org.bouncycastle:bcprov-jdk15on:1.67
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:3.21.0
|
||||
org.checkerframework:checker-qual:3.21.3
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.20
|
||||
|
||||
@@ -17,6 +17,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.6
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
@@ -125,7 +126,7 @@ com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.code.gson:gson:2.8.9
|
||||
com.google.common.html.types:types:1.0.6
|
||||
com.google.dagger:dagger:2.33
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.errorprone:error_prone_annotations:2.11.0
|
||||
com.google.escapevelocity:escapevelocity:0.9.1
|
||||
com.google.flatbuffers:flatbuffers-java:1.12.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
@@ -274,7 +275,7 @@ org.bouncycastle:bcpg-jdk15on:1.67
|
||||
org.bouncycastle:bcpkix-jdk15on:1.67
|
||||
org.bouncycastle:bcprov-jdk15on:1.67
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:3.21.0
|
||||
org.checkerframework:checker-qual:3.21.3
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.20
|
||||
|
||||
@@ -112,6 +112,12 @@ public class BatchModule {
|
||||
req, ExpandRecurringBillingEventsAction.PARAM_CURSOR_TIME);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(ResaveAllEppResourcesPipelineAction.PARAM_FAST)
|
||||
static Optional<Boolean> provideIsFast(HttpServletRequest req) {
|
||||
return extractOptionalBooleanParameter(req, ResaveAllEppResourcesPipelineAction.PARAM_FAST);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named(QUEUE_ASYNC_ACTIONS)
|
||||
static Queue provideAsyncActionsPushQueue() {
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.batch;
|
||||
|
||||
import static google.registry.beam.BeamUtils.createJobName;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.api.services.dataflow.Dataflow;
|
||||
import com.google.api.services.dataflow.model.LaunchFlexTemplateParameter;
|
||||
import com.google.api.services.dataflow.model.LaunchFlexTemplateRequest;
|
||||
import com.google.api.services.dataflow.model.LaunchFlexTemplateResponse;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Clock;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Starts a Dataflow pipeline that resaves all EPP resources projected to the current time.
|
||||
*
|
||||
* <p>This is useful for a few situations. First, as a fallback option for resource transfers that
|
||||
* have expired pending transfers (this will resolve them), just in case the enqueued action fails.
|
||||
* Second, it will reflect domain autorenews that have happened. Third, it will remove any expired
|
||||
* grace periods.
|
||||
*
|
||||
* <p>There's also the general principle that it's good to have the data in the database remain as
|
||||
* current as is reasonably possible.
|
||||
*
|
||||
* <p>If the <code>?isFast=true</code> query string parameter is passed as true, the pipeline will
|
||||
* only attempt to load, project, and resave entities where we expect one of the previous situations
|
||||
* has occurred. Otherwise, we will load, project, and resave all EPP resources.
|
||||
*
|
||||
* <p>This runs the {@link google.registry.beam.resave.ResaveAllEppResourcesPipeline}.
|
||||
*/
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
path = ResaveAllEppResourcesPipelineAction.PATH,
|
||||
auth = Auth.AUTH_INTERNAL_OR_ADMIN)
|
||||
public class ResaveAllEppResourcesPipelineAction implements Runnable {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
static final String PATH = "/_dr/task/resaveAllEppResourcesPipeline";
|
||||
static final String PIPELINE_NAME = "resave_all_epp_resources_pipeline";
|
||||
|
||||
public static final String PARAM_FAST = "fast";
|
||||
|
||||
private final String projectId;
|
||||
private final String jobRegion;
|
||||
private final String stagingBucketUrl;
|
||||
private final boolean fast;
|
||||
private final Clock clock;
|
||||
private final Response response;
|
||||
private final Dataflow dataflow;
|
||||
|
||||
@Inject
|
||||
ResaveAllEppResourcesPipelineAction(
|
||||
@Config("projectId") String projectId,
|
||||
@Config("defaultJobRegion") String jobRegion,
|
||||
@Config("beamStagingBucketUrl") String stagingBucketUrl,
|
||||
@Parameter(PARAM_FAST) Optional<Boolean> fast,
|
||||
Clock clock,
|
||||
Response response,
|
||||
Dataflow dataflow) {
|
||||
this.projectId = projectId;
|
||||
this.jobRegion = jobRegion;
|
||||
this.stagingBucketUrl = stagingBucketUrl;
|
||||
this.fast = fast.orElse(false);
|
||||
this.clock = clock;
|
||||
this.response = response;
|
||||
this.dataflow = dataflow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
||||
logger.atInfo().log("Launching ResaveAllEppResourcesPipeline");
|
||||
try {
|
||||
LaunchFlexTemplateParameter parameter =
|
||||
new LaunchFlexTemplateParameter()
|
||||
.setJobName(createJobName("resave-all-epp-resources", clock))
|
||||
.setContainerSpecGcsPath(
|
||||
String.format("%s/%s_metadata.json", stagingBucketUrl, PIPELINE_NAME))
|
||||
.setParameters(
|
||||
new ImmutableMap.Builder<String, String>()
|
||||
.put(PARAM_FAST, Boolean.toString(fast))
|
||||
.put("registryEnvironment", RegistryEnvironment.get().name())
|
||||
.build());
|
||||
LaunchFlexTemplateResponse launchResponse =
|
||||
dataflow
|
||||
.projects()
|
||||
.locations()
|
||||
.flexTemplates()
|
||||
.launch(
|
||||
projectId,
|
||||
jobRegion,
|
||||
new LaunchFlexTemplateRequest().setLaunchParameter(parameter))
|
||||
.execute();
|
||||
logger.atInfo().log("Got response: %s", launchResponse.getJob().toPrettyString());
|
||||
String jobId = launchResponse.getJob().getId();
|
||||
response.setStatus(SC_OK);
|
||||
response.setPayload(String.format("Launched resaveAllEppResources pipeline: %s", jobId));
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log("Template Launch failed.");
|
||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
response.setPayload(String.format("Pipeline launch failed: %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,7 +158,7 @@ public class InitSqlPipeline implements Serializable {
|
||||
.addAll(toKindStrings(PHASE_TWO_ORDERED))
|
||||
.build()));
|
||||
|
||||
// Set up the pipeline to write entity kinds from PHASE_ONE_ORDERED to SQL. Return a object
|
||||
// Set up the pipeline to write entity kinds from PHASE_ONE_ORDERED to SQL. Return an object
|
||||
// that signals the completion of the phase.
|
||||
PCollection<Void> blocker =
|
||||
scheduleOnePhaseWrites(datastoreSnapshot, PHASE_ONE_ORDERED, Optional.empty(), null);
|
||||
|
||||
@@ -22,7 +22,7 @@ import static google.registry.beam.initsql.BackupPaths.getExportFilePatterns;
|
||||
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static org.apache.beam.sdk.values.TypeDescriptors.kvs;
|
||||
import static org.apache.beam.sdk.values.TypeDescriptors.strings;
|
||||
@@ -343,11 +343,11 @@ public final class Transforms {
|
||||
// Canonicalize old domain/host names from 2016 and earlier before we were enforcing this.
|
||||
entity.setIndexedProperty(
|
||||
"fullyQualifiedDomainName",
|
||||
canonicalizeDomainName((String) entity.getProperty("fullyQualifiedDomainName")));
|
||||
canonicalizeHostname((String) entity.getProperty("fullyQualifiedDomainName")));
|
||||
} else if (entity.getKind().equals("HostResource")) {
|
||||
entity.setIndexedProperty(
|
||||
"fullyQualifiedHostName",
|
||||
canonicalizeDomainName((String) entity.getProperty("fullyQualifiedHostName")));
|
||||
canonicalizeHostname((String) entity.getProperty("fullyQualifiedHostName")));
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.beam.invoicing;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.beam.BeamUtils.getQueryFromFile;
|
||||
import static org.apache.beam.sdk.values.TypeDescriptors.strings;
|
||||
@@ -53,6 +54,7 @@ import org.apache.beam.sdk.transforms.PTransform;
|
||||
import org.apache.beam.sdk.values.KV;
|
||||
import org.apache.beam.sdk.values.PCollection;
|
||||
import org.apache.beam.sdk.values.TypeDescriptor;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
/**
|
||||
* Definition of a Dataflow Flex pipeline template, which generates a given month's invoices.
|
||||
@@ -122,12 +124,19 @@ public class InvoicingPipeline implements Serializable {
|
||||
google.registry.model.billing.BillingEvent.OneTime oneTime =
|
||||
(google.registry.model.billing.BillingEvent.OneTime) row[0];
|
||||
Registrar registrar = (Registrar) row[1];
|
||||
CurrencyUnit currency = oneTime.getCost().getCurrencyUnit();
|
||||
checkState(
|
||||
registrar.getBillingAccountMap().containsKey(currency),
|
||||
"Registrar %s does not have a product account key for the currency unit: %s",
|
||||
registrar.getRegistrarId(),
|
||||
currency);
|
||||
|
||||
return BillingEvent.create(
|
||||
oneTime.getId(),
|
||||
DateTimeUtils.toZonedDateTime(oneTime.getBillingTime(), ZoneId.of("UTC")),
|
||||
DateTimeUtils.toZonedDateTime(oneTime.getEventTime(), ZoneId.of("UTC")),
|
||||
registrar.getRegistrarId(),
|
||||
registrar.getBillingIdentifier().toString(),
|
||||
registrar.getBillingAccountMap().get(currency),
|
||||
registrar.getPoNumber().orElse(""),
|
||||
DomainNameUtils.getTldFromDomainName(oneTime.getTargetId()),
|
||||
oneTime.getReason().toString(),
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.beam.rde;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.beam.rde.RdePipeline.TupleTags.DOMAIN_FRAGMENTS;
|
||||
import static google.registry.beam.rde.RdePipeline.TupleTags.EXTERNAL_HOST_FRAGMENTS;
|
||||
@@ -28,10 +29,12 @@ import static google.registry.model.reporting.HistoryEntryDao.RESOURCE_TYPES_TO_
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static org.apache.beam.sdk.values.TypeDescriptors.kvs;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import dagger.BindsInstance;
|
||||
import dagger.Component;
|
||||
@@ -198,6 +201,8 @@ public class RdePipeline implements Serializable {
|
||||
HostHistory.class,
|
||||
"hostBase");
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@Inject
|
||||
RdePipeline(RdePipelineOptions options, GcsUtils gcsUtils, CloudTasksUtils cloudTasksUtils) {
|
||||
this.options = options;
|
||||
@@ -357,6 +362,33 @@ public class RdePipeline implements Serializable {
|
||||
.setCoder(KvCoder.of(StringUtf8Coder.of(), VarLongCoder.of()));
|
||||
}
|
||||
|
||||
private <T extends HistoryEntry> EppResource loadResourceByHistoryEntryId(
|
||||
Class<T> historyEntryClazz, String repoId, Iterable<Long> revisionIds) {
|
||||
ImmutableList<Long> ids = ImmutableList.copyOf(revisionIds);
|
||||
// The size should always be 1 because we are only getting one repo ID -> revision ID pair per
|
||||
// repo ID from the source transform (the JPA query in the method above). But for some reason
|
||||
// after CoGroupByKey (joining the revision IDs and the pending deposits on repo IDs), in
|
||||
// #removedUnreferencedResources, duplicate revision IDs are sometimes introduced. Here we
|
||||
// attempt to deduplicate the iterable. If it contains multiple revision IDs that are NOT the
|
||||
// same, we have a more serious problem as we cannot be sure which one to use. We should use the
|
||||
// highest revision ID, but we don't even know where it comes from, as the query should
|
||||
// definitively only give us one revision ID per repo ID. In this case we have to abort and
|
||||
// require manual intervention.
|
||||
if (ids.size() != 1) {
|
||||
ImmutableSet<Long> dedupedIds = ImmutableSet.copyOf(ids);
|
||||
checkState(
|
||||
dedupedIds.size() == 1,
|
||||
"Multiple unique revision IDs detected for %s repo ID %s: %s",
|
||||
EPP_RESOURCE_FIELD_NAME.get(historyEntryClazz),
|
||||
repoId,
|
||||
ids);
|
||||
logger.atSevere().log(
|
||||
"Duplicate revision IDs detected for %s repo ID %s: %s",
|
||||
EPP_RESOURCE_FIELD_NAME.get(historyEntryClazz), repoId, ids);
|
||||
}
|
||||
return loadResourceByHistoryEntryId(historyEntryClazz, repoId, ids.get(0));
|
||||
}
|
||||
|
||||
private <T extends HistoryEntry> EppResource loadResourceByHistoryEntryId(
|
||||
Class<T> historyEntryClazz, String repoId, long revisionId) {
|
||||
try {
|
||||
@@ -516,7 +548,7 @@ public class RdePipeline implements Serializable {
|
||||
loadResourceByHistoryEntryId(
|
||||
ContactHistory.class,
|
||||
kv.getKey(),
|
||||
kv.getValue().getOnly(REVISION_ID));
|
||||
kv.getValue().getAll(REVISION_ID));
|
||||
DepositFragment fragment = marshaller.marshalContact(contact);
|
||||
ImmutableSet<KV<PendingDeposit, DepositFragment>> fragments =
|
||||
Streams.stream(kv.getValue().getAll(PENDING_DEPOSIT))
|
||||
@@ -549,8 +581,8 @@ public class RdePipeline implements Serializable {
|
||||
loadResourceByHistoryEntryId(
|
||||
HostHistory.class,
|
||||
kv.getKey(),
|
||||
kv.getValue().getOnly(REVISION_ID));
|
||||
// When a host is subordinate, we need to find it's superordinate domain and
|
||||
kv.getValue().getAll(REVISION_ID));
|
||||
// When a host is subordinate, we need to find its superordinate domain and
|
||||
// include it in the deposit as well.
|
||||
if (host.isSubordinate()) {
|
||||
subordinateHostCounter.inc();
|
||||
@@ -627,7 +659,7 @@ public class RdePipeline implements Serializable {
|
||||
loadResourceByHistoryEntryId(
|
||||
DomainHistory.class,
|
||||
kv.getKey(),
|
||||
kv.getValue().getOnly(REVISION_ID));
|
||||
kv.getValue().getAll(REVISION_ID));
|
||||
ImmutableSet.Builder<KV<PendingDeposit, DepositFragment>> results =
|
||||
new ImmutableSet.Builder<>();
|
||||
for (KV<String, CoGbkResult> hostToPendingDeposits :
|
||||
@@ -637,7 +669,7 @@ public class RdePipeline implements Serializable {
|
||||
loadResourceByHistoryEntryId(
|
||||
HostHistory.class,
|
||||
hostToPendingDeposits.getKey(),
|
||||
hostToPendingDeposits.getValue().getOnly(REVISION_ID));
|
||||
hostToPendingDeposits.getValue().getAll(REVISION_ID));
|
||||
DepositFragment fragment =
|
||||
marshaller.marshalSubordinateHost(host, superordinateDomain);
|
||||
Streams.stream(hostToPendingDeposits.getValue().getAll(PENDING_DEPOSIT))
|
||||
@@ -696,6 +728,7 @@ public class RdePipeline implements Serializable {
|
||||
* CoGbkResult}s are used.
|
||||
*/
|
||||
protected abstract static class TupleTags {
|
||||
|
||||
protected static final TupleTag<KV<PendingDeposit, DepositFragment>> DOMAIN_FRAGMENTS =
|
||||
new TupleTag<KV<PendingDeposit, DepositFragment>>() {};
|
||||
|
||||
@@ -729,10 +762,12 @@ public class RdePipeline implements Serializable {
|
||||
UtilsModule.class
|
||||
})
|
||||
interface RdePipelineComponent {
|
||||
|
||||
RdePipeline rdePipeline();
|
||||
|
||||
@Component.Builder
|
||||
interface Builder {
|
||||
|
||||
@BindsInstance
|
||||
Builder options(RdePipelineOptions options);
|
||||
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.beam.resave;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static org.apache.beam.sdk.values.TypeDescriptors.integers;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.beam.common.RegistryJpaIO;
|
||||
import google.registry.beam.common.RegistryJpaIO.Read;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import java.io.Serializable;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import org.apache.beam.sdk.Pipeline;
|
||||
import org.apache.beam.sdk.PipelineResult;
|
||||
import org.apache.beam.sdk.transforms.DoFn;
|
||||
import org.apache.beam.sdk.transforms.GroupIntoBatches;
|
||||
import org.apache.beam.sdk.transforms.ParDo;
|
||||
import org.apache.beam.sdk.transforms.WithKeys;
|
||||
import org.apache.beam.sdk.util.ShardedKey;
|
||||
import org.apache.beam.sdk.values.KV;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* A Dataflow Flex pipeline that resaves changed EPP resources in SQL.
|
||||
*
|
||||
* <p>Due to the way that Hibernate works, if an entity is unchanged by {@link
|
||||
* EppResource#cloneProjectedAtTime(DateTime)} it will not actually be re-persisted to the database.
|
||||
* Thus, the only actual changes occur when objects are changed by projecting them to now, such as
|
||||
* when a pending transfer is resolved.
|
||||
*/
|
||||
public class ResaveAllEppResourcesPipeline implements Serializable {
|
||||
|
||||
private static final ImmutableSet<Class<? extends EppResource>> EPP_RESOURCE_CLASSES =
|
||||
ImmutableSet.of(ContactResource.class, DomainBase.class, HostResource.class);
|
||||
|
||||
/**
|
||||
* There exist three possible situations where we know we'll want to project domains to the
|
||||
* current point in time:
|
||||
*
|
||||
* <ul>
|
||||
* <li>A pending domain transfer has expired.
|
||||
* <li>A domain is past its expiration time without being deleted (this means it autorenewed).
|
||||
* <li>A domain has expired grace periods.
|
||||
* </ul>
|
||||
*
|
||||
* <p>This command contains all three scenarios so that we can avoid querying the Domain table
|
||||
* multiple times, and to avoid projecting and resaving the same domain multiple times.
|
||||
*/
|
||||
private static final String DOMAINS_TO_PROJECT_QUERY =
|
||||
"FROM Domain d WHERE (d.transferData.transferStatus = 'PENDING' AND"
|
||||
+ " d.transferData.pendingTransferExpirationTime < current_timestamp()) OR"
|
||||
+ " (d.registrationExpirationTime < current_timestamp() AND d.deletionTime ="
|
||||
+ " (:END_OF_TIME)) OR (EXISTS (SELECT 1 FROM GracePeriod gp WHERE gp.domainRepoId ="
|
||||
+ " d.repoId AND gp.expirationTime < current_timestamp()))";
|
||||
|
||||
private final ResaveAllEppResourcesPipelineOptions options;
|
||||
|
||||
ResaveAllEppResourcesPipeline(ResaveAllEppResourcesPipelineOptions options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
PipelineResult run() {
|
||||
Pipeline pipeline = Pipeline.create(options);
|
||||
setupPipeline(pipeline);
|
||||
return pipeline.run();
|
||||
}
|
||||
|
||||
void setupPipeline(Pipeline pipeline) {
|
||||
options.setIsolationOverride(TransactionIsolationLevel.TRANSACTION_READ_COMMITTED);
|
||||
if (options.getFast()) {
|
||||
fastResaveContacts(pipeline);
|
||||
fastResaveDomains(pipeline);
|
||||
} else {
|
||||
EPP_RESOURCE_CLASSES.forEach(clazz -> forceResaveAllResources(pipeline, clazz));
|
||||
}
|
||||
}
|
||||
|
||||
/** Projects to the current time and saves any contacts with expired transfers. */
|
||||
private void fastResaveContacts(Pipeline pipeline) {
|
||||
Read<ContactResource, ContactResource> read =
|
||||
RegistryJpaIO.read(
|
||||
"FROM Contact WHERE transferData.transferStatus = 'PENDING' AND"
|
||||
+ " transferData.pendingTransferExpirationTime < current_timestamp()",
|
||||
ContactResource.class,
|
||||
c -> c);
|
||||
projectAndResaveResources(pipeline, ContactResource.class, read);
|
||||
}
|
||||
|
||||
/**
|
||||
* Projects to the current time and saves any domains with expired pending actions (e.g.
|
||||
* transfers, grace periods).
|
||||
*
|
||||
* <p>The logic of what might have changed is paraphrased from {@link
|
||||
* google.registry.model.domain.DomainContent#cloneProjectedAtTime(DateTime)}.
|
||||
*/
|
||||
private void fastResaveDomains(Pipeline pipeline) {
|
||||
Read<DomainBase, DomainBase> read =
|
||||
RegistryJpaIO.read(
|
||||
DOMAINS_TO_PROJECT_QUERY,
|
||||
ImmutableMap.of("END_OF_TIME", DateTimeUtils.END_OF_TIME),
|
||||
DomainBase.class,
|
||||
d -> d);
|
||||
projectAndResaveResources(pipeline, DomainBase.class, read);
|
||||
}
|
||||
|
||||
/** Projects all resources to the current time and saves them. */
|
||||
private <T extends EppResource> void forceResaveAllResources(Pipeline pipeline, Class<T> clazz) {
|
||||
Read<T, T> read = RegistryJpaIO.read(() -> CriteriaQueryBuilder.create(clazz).build());
|
||||
projectAndResaveResources(pipeline, clazz, read);
|
||||
}
|
||||
|
||||
/** Projects and re-saves the result of the provided {@link Read}. */
|
||||
private <T extends EppResource> void projectAndResaveResources(
|
||||
Pipeline pipeline, Class<T> clazz, Read<?, T> read) {
|
||||
int numShards = options.getSqlWriteShards();
|
||||
int batchSize = options.getSqlWriteBatchSize();
|
||||
String className = clazz.getSimpleName();
|
||||
pipeline
|
||||
.apply("Read " + className, read)
|
||||
.apply(
|
||||
"Shard data for class" + className,
|
||||
WithKeys.<Integer, T>of(e -> ThreadLocalRandom.current().nextInt(numShards))
|
||||
.withKeyType(integers()))
|
||||
.apply(
|
||||
"Group into batches for class" + className,
|
||||
GroupIntoBatches.<Integer, T>ofSize(batchSize).withShardedKey())
|
||||
.apply("Map " + className + " to now", ParDo.of(new BatchedProjectionFunction<>()))
|
||||
.apply(
|
||||
"Write transformed " + className,
|
||||
RegistryJpaIO.<EppResource>write()
|
||||
.withName("Write transformed " + className)
|
||||
.withBatchSize(batchSize)
|
||||
.withShards(numShards));
|
||||
}
|
||||
|
||||
private static class BatchedProjectionFunction<T extends EppResource>
|
||||
extends DoFn<KV<ShardedKey<Integer>, Iterable<T>>, EppResource> {
|
||||
|
||||
@ProcessElement
|
||||
public void processElement(
|
||||
@Element KV<ShardedKey<Integer>, Iterable<T>> element,
|
||||
OutputReceiver<EppResource> outputReceiver) {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
element
|
||||
.getValue()
|
||||
.forEach(
|
||||
resource ->
|
||||
outputReceiver.output(
|
||||
resource.cloneProjectedAtTime(jpaTm().getTransactionTime()))));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.beam.resave;
|
||||
|
||||
import google.registry.beam.common.RegistryPipelineOptions;
|
||||
import org.apache.beam.sdk.options.Description;
|
||||
|
||||
public interface ResaveAllEppResourcesPipelineOptions extends RegistryPipelineOptions {
|
||||
|
||||
@Description("True if we should attempt to run only over potentially out-of-date EPP resources")
|
||||
boolean getFast();
|
||||
|
||||
void setFast(boolean fast);
|
||||
}
|
||||
@@ -1438,8 +1438,8 @@ public final class RegistryConfig {
|
||||
}
|
||||
|
||||
/** Returns the amount of time a singleton should be cached, before expiring. */
|
||||
public static Duration getSingletonCacheRefreshDuration() {
|
||||
return Duration.standardSeconds(CONFIG_SETTINGS.get().caching.singletonCacheRefreshSeconds);
|
||||
public static java.time.Duration getSingletonCacheRefreshDuration() {
|
||||
return java.time.Duration.ofSeconds(CONFIG_SETTINGS.get().caching.singletonCacheRefreshSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -316,6 +316,12 @@
|
||||
<url-pattern>/_dr/task/resaveAllEppResources</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Dataflow pipeline to re-save all EPP resources. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>backend-servlet</servlet-name>
|
||||
<url-pattern>/_dr/task/resaveAllEppResourcesPipeline</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Reread all Registrar RDAP Base Urls from the ICANN endpoint. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>backend-servlet</servlet-name>
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<blacklistentries>
|
||||
|
||||
<!-- Example IPv4 CIDR Subnet
|
||||
<blacklist>
|
||||
<subnet>1.2.3.4/24</subnet>
|
||||
<description>An IPv4 subnet</description>
|
||||
</blacklist> -->
|
||||
|
||||
<!-- Example IPv6 CIDR Subnet
|
||||
<blacklist>
|
||||
<subnet>abcd::123:4567/48</subnet>
|
||||
<description>An IPv6 subnet</description>
|
||||
</blacklist> -->
|
||||
|
||||
</blacklistentries>
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<blacklistentries>
|
||||
|
||||
<!-- Example IPv4 CIDR Subnet
|
||||
<blacklist>
|
||||
<subnet>1.2.3.4/24</subnet>
|
||||
<description>An IPv4 subnet</description>
|
||||
</blacklist> -->
|
||||
|
||||
<!-- Example IPv6 CIDR Subnet
|
||||
<blacklist>
|
||||
<subnet>abcd::123:4567/48</subnet>
|
||||
<description>An IPv6 subnet</description>
|
||||
</blacklist> -->
|
||||
|
||||
</blacklistentries>
|
||||
@@ -31,7 +31,7 @@ import static google.registry.monitoring.whitebox.CheckApiMetric.Status.UNKNOWN_
|
||||
import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.PREMIUM;
|
||||
import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.STANDARD;
|
||||
import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static org.json.simple.JSONValue.toJSONString;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -105,7 +105,7 @@ public class CheckApiAction implements Runnable {
|
||||
String domainString;
|
||||
InternetDomainName domainName;
|
||||
try {
|
||||
domainString = canonicalizeDomainName(nullToEmpty(domain));
|
||||
domainString = canonicalizeHostname(nullToEmpty(domain));
|
||||
domainName = validateDomainName(domainString);
|
||||
} catch (IllegalArgumentException | EppException e) {
|
||||
metricBuilder.status(INVALID_NAME);
|
||||
|
||||
@@ -25,6 +25,7 @@ import static google.registry.model.ResourceTransferUtils.handlePendingTransferO
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING;
|
||||
import static google.registry.model.transfer.TransferStatus.SERVER_CANCELLED;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.assertAsyncActionsAreAllowed;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -94,6 +95,7 @@ public final class ContactDeleteFlow implements TransactionalFlow {
|
||||
extensionManager.register(MetadataExtension.class);
|
||||
validateRegistrarIsLoggedIn(registrarId);
|
||||
extensionManager.validate();
|
||||
assertAsyncActionsAreAllowed();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
checkLinkedDomains(targetId, now, ContactResource.class, DomainBase::getReferencedContacts);
|
||||
ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now);
|
||||
|
||||
@@ -22,6 +22,7 @@ import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||
import static google.registry.flows.host.HostFlowUtils.validateHostName;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.assertAsyncActionsAreAllowed;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -96,6 +97,7 @@ public final class HostDeleteFlow implements TransactionalFlow {
|
||||
extensionManager.register(MetadataExtension.class);
|
||||
validateRegistrarIsLoggedIn(registrarId);
|
||||
extensionManager.validate();
|
||||
assertAsyncActionsAreAllowed();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
validateHostName(targetId);
|
||||
checkLinkedDomains(targetId, now, HostResource.class, DomainBase::getNameservers);
|
||||
|
||||
@@ -28,6 +28,7 @@ import static google.registry.flows.host.HostFlowUtils.verifySuperordinateDomain
|
||||
import static google.registry.flows.host.HostFlowUtils.verifySuperordinateDomainOwnership;
|
||||
import static google.registry.model.index.ForeignKeyIndex.loadAndGetKey;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.HOST_UPDATE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.assertAsyncActionsAreAllowed;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
|
||||
@@ -136,6 +137,9 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
validateHostName(targetId);
|
||||
HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now);
|
||||
boolean isHostRename = suppliedNewHostName != null;
|
||||
if (isHostRename) {
|
||||
assertAsyncActionsAreAllowed();
|
||||
}
|
||||
String oldHostName = targetId;
|
||||
String newHostName = firstNonNull(suppliedNewHostName, oldHostName);
|
||||
DomainBase oldSuperordinateDomain =
|
||||
|
||||
@@ -17,10 +17,10 @@ package google.registry.model;
|
||||
import static com.google.common.base.Suppliers.memoizeWithExpiration;
|
||||
import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.joda.time.Duration.ZERO;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.google.common.base.Supplier;
|
||||
import org.joda.time.Duration;
|
||||
import java.time.Duration;
|
||||
|
||||
/** Utility methods related to caching Datastore entities. */
|
||||
public class CacheUtils {
|
||||
@@ -41,8 +41,36 @@ public class CacheUtils {
|
||||
*/
|
||||
public static <T> Supplier<T> tryMemoizeWithExpiration(
|
||||
Duration expiration, Supplier<T> original) {
|
||||
return expiration.isEqual(ZERO)
|
||||
return expiration.isZero()
|
||||
? original
|
||||
: memoizeWithExpiration(original, expiration.getMillis(), MILLISECONDS);
|
||||
: memoizeWithExpiration(original, expiration.toMillis(), MILLISECONDS);
|
||||
}
|
||||
|
||||
/** Creates and returns a new {@link Caffeine} builder. */
|
||||
public static Caffeine<Object, Object> newCacheBuilder() {
|
||||
return Caffeine.newBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a new {@link Caffeine} builder with the specified cache expiration.
|
||||
*
|
||||
* <p>This also sets the refresh duration to half of the cache expiration. The resultant behavior
|
||||
* is that a cache entry is eligible to be asynchronously refreshed after access once more than
|
||||
* half of its cache duration has elapsed, and then it is synchronously refreshed (blocking the
|
||||
* read) once its full cache duration has elapsed. So you will never get data older than the cache
|
||||
* expiration, but for frequently accessed keys it will be refreshed more often than that and the
|
||||
* cost of the load will never be incurred during the read.
|
||||
*/
|
||||
public static Caffeine<Object, Object> newCacheBuilder(Duration expireAfterWrite) {
|
||||
Duration refreshAfterWrite = expireAfterWrite.dividedBy(2);
|
||||
Caffeine<Object, Object> caffeine = Caffeine.newBuilder().expireAfterWrite(expireAfterWrite);
|
||||
// In tests, the cache duration is usually set to 0, which means the cache load synchronously
|
||||
// blocks every time it is called anyway because of the expireAfterWrite() above. Thus, setting
|
||||
// the refreshAfterWrite won't do anything, plus it's not legal to call it with a zero value
|
||||
// anyway (Caffeine allows expireAfterWrite to be zero but not refreshAfterWrite).
|
||||
if (!refreshAfterWrite.isZero()) {
|
||||
caffeine = caffeine.refreshAfterWrite(refreshAfterWrite);
|
||||
}
|
||||
return caffeine;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,40 @@ public abstract class BillingEvent extends ImmutableObject
|
||||
* This flag will be added to any {@link OneTime} events that are created via, e.g., an
|
||||
* automated process to expand {@link Recurring} events.
|
||||
*/
|
||||
SYNTHETIC
|
||||
SYNTHETIC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets of renewal price behaviors that can be applied to billing recurrences.
|
||||
*
|
||||
* <p>When a client renews a domain, they could be charged differently, depending on factors such
|
||||
* as the client type and the domain itself.
|
||||
*/
|
||||
public enum RenewalPriceBehavior {
|
||||
/**
|
||||
* This indicates the renewal price is the default price.
|
||||
*
|
||||
* <p>By default, if the domain is premium, then premium price will be used. Otherwise, the
|
||||
* standard price of the TLD will be used.
|
||||
*/
|
||||
DEFAULT,
|
||||
/**
|
||||
* This indicates the domain will be renewed at standard price even if it's a premium domain.
|
||||
*
|
||||
* <p>We chose to name this "NONPREMIUM" rather than simply "STANDARD" to avoid confusion
|
||||
* between "STANDARD" and "DEFAULT".
|
||||
*
|
||||
* <p>This price behavior is used with anchor tenants.
|
||||
*/
|
||||
NONPREMIUM,
|
||||
/**
|
||||
* This indicates that the renewalPrice in {@link BillingEvent.Recurring} will be used for
|
||||
* domain renewal.
|
||||
*
|
||||
* <p>The renewalPrice has a non-null value iff the price behavior is set to "SPECIFIED". This
|
||||
* behavior is used with internal registrations.
|
||||
*/
|
||||
SPECIFIED;
|
||||
}
|
||||
|
||||
/** Entity id. */
|
||||
@@ -555,6 +588,22 @@ public abstract class BillingEvent extends ImmutableObject
|
||||
})
|
||||
TimeOfYear recurrenceTimeOfYear;
|
||||
|
||||
/**
|
||||
* The renewal price for domain renewal if and only if it's specified.
|
||||
*
|
||||
* <p>This price column remains null except when the renewal price behavior of the billing is
|
||||
* SPECIFIED. This column is used for internal registrations.
|
||||
*/
|
||||
@Nullable
|
||||
@Type(type = JodaMoneyType.TYPE_NAME)
|
||||
@Columns(
|
||||
columns = {@Column(name = "renewalPriceAmount"), @Column(name = "renewalPriceCurrency")})
|
||||
Money renewalPrice;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "renewalPriceBehavior", nullable = false)
|
||||
RenewalPriceBehavior renewalPriceBehavior = RenewalPriceBehavior.DEFAULT;
|
||||
|
||||
public DateTime getRecurrenceEndTime() {
|
||||
return recurrenceEndTime;
|
||||
}
|
||||
@@ -563,6 +612,14 @@ public abstract class BillingEvent extends ImmutableObject
|
||||
return recurrenceTimeOfYear;
|
||||
}
|
||||
|
||||
public RenewalPriceBehavior getRenewalPriceBehavior() {
|
||||
return renewalPriceBehavior;
|
||||
}
|
||||
|
||||
public Optional<Money> getRenewalPrice() {
|
||||
return Optional.ofNullable(renewalPrice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VKey<Recurring> createVKey() {
|
||||
return VKey.create(Recurring.class, getId(), Key.create(this));
|
||||
@@ -591,11 +648,26 @@ public abstract class BillingEvent extends ImmutableObject
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRenewalPriceBehavior(RenewalPriceBehavior renewalPriceBehavior) {
|
||||
getInstance().renewalPriceBehavior = renewalPriceBehavior;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRenewalPrice(@Nullable Money renewalPrice) {
|
||||
getInstance().renewalPrice = renewalPrice;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Recurring build() {
|
||||
Recurring instance = getInstance();
|
||||
checkNotNull(instance.eventTime);
|
||||
checkNotNull(instance.reason);
|
||||
checkArgument(
|
||||
(instance.renewalPriceBehavior == RenewalPriceBehavior.SPECIFIED)
|
||||
^ (instance.renewalPrice == null),
|
||||
"Renewal price can have a value if and only if the renewal price behavior is"
|
||||
+ " SPECIFIED");
|
||||
instance.recurrenceTimeOfYear = TimeOfYear.fromDateTime(instance.eventTime);
|
||||
instance.recurrenceEndTime =
|
||||
Optional.ofNullable(instance.recurrenceEndTime).orElse(END_OF_TIME);
|
||||
|
||||
@@ -30,6 +30,7 @@ import google.registry.model.annotations.DeleteAfterMigration;
|
||||
import google.registry.model.common.TimedTransitionProperty.TimedTransition;
|
||||
import google.registry.model.replay.SqlOnlyEntity;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.PersistenceException;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -62,11 +63,28 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements
|
||||
* not the phase is read-only.
|
||||
*/
|
||||
public enum MigrationState {
|
||||
/** Datastore is the only DB being used. */
|
||||
DATASTORE_ONLY(PrimaryDatabase.DATASTORE, false, ReplayDirection.NO_REPLAY),
|
||||
|
||||
/** Datastore is the primary DB, with changes replicated to Cloud SQL. */
|
||||
DATASTORE_PRIMARY(PrimaryDatabase.DATASTORE, false, ReplayDirection.DATASTORE_TO_SQL),
|
||||
|
||||
/** Datastore is the primary DB, with replication, and async actions are disallowed. */
|
||||
DATASTORE_PRIMARY_NO_ASYNC(PrimaryDatabase.DATASTORE, false, ReplayDirection.DATASTORE_TO_SQL),
|
||||
|
||||
/** Datastore is the primary DB, with replication, and all mutating actions are disallowed. */
|
||||
DATASTORE_PRIMARY_READ_ONLY(PrimaryDatabase.DATASTORE, true, ReplayDirection.DATASTORE_TO_SQL),
|
||||
|
||||
/**
|
||||
* Cloud SQL is the primary DB, with replication back to Datastore, and all mutating actions are
|
||||
* disallowed.
|
||||
*/
|
||||
SQL_PRIMARY_READ_ONLY(PrimaryDatabase.CLOUD_SQL, true, ReplayDirection.SQL_TO_DATASTORE),
|
||||
|
||||
/** Cloud SQL is the primary DB, with changes replicated to Datastore. */
|
||||
SQL_PRIMARY(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.SQL_TO_DATASTORE),
|
||||
|
||||
/** Cloud SQL is the only DB being used. */
|
||||
SQL_ONLY(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY);
|
||||
|
||||
private final PrimaryDatabase primaryDatabase;
|
||||
@@ -146,11 +164,17 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements
|
||||
.putAll(
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
MigrationState.DATASTORE_ONLY,
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.putAll(
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
|
||||
MigrationState.DATASTORE_ONLY,
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.putAll(
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
|
||||
MigrationState.DATASTORE_ONLY,
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
|
||||
MigrationState.SQL_PRIMARY_READ_ONLY,
|
||||
MigrationState.SQL_PRIMARY)
|
||||
.putAll(
|
||||
@@ -165,10 +189,9 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements
|
||||
MigrationState.SQL_ONLY,
|
||||
MigrationState.SQL_PRIMARY_READ_ONLY,
|
||||
MigrationState.SQL_PRIMARY);
|
||||
|
||||
// In addition, we can always transition from a state to itself (useful when updating the map).
|
||||
for (MigrationState migrationState : MigrationState.values()) {
|
||||
builder.put(migrationState, migrationState);
|
||||
}
|
||||
Arrays.stream(MigrationState.values()).forEach(state -> builder.put(state, state));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -246,7 +269,7 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements
|
||||
* A provided map of transitions may be valid by itself (i.e. it shifts states properly, doesn't
|
||||
* skip states, and doesn't backtrack incorrectly) while still being invalid. In addition to the
|
||||
* transitions in the map being valid, the single transition from the current map at the current
|
||||
* time to the new map at the current time time must also be valid.
|
||||
* time to the new map at the current time must also be valid.
|
||||
*/
|
||||
private static void validateTransitionAtCurrentTime(
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition> newTransitions) {
|
||||
|
||||
@@ -175,7 +175,7 @@ public class DomainBase extends DomainContent
|
||||
|
||||
@Override
|
||||
public void beforeSqlSaveOnReplay() {
|
||||
fullyQualifiedDomainName = DomainNameUtils.canonicalizeDomainName(fullyQualifiedDomainName);
|
||||
fullyQualifiedDomainName = DomainNameUtils.canonicalizeHostname(fullyQualifiedDomainName);
|
||||
dsData =
|
||||
dsData.stream()
|
||||
.filter(datum -> datum.getDigest() != null && datum.getDigest().length > 0)
|
||||
|
||||
@@ -34,7 +34,7 @@ import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.earliestOf;
|
||||
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static google.registry.util.DomainNameUtils.getTldFromDomainName;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
@@ -890,7 +890,7 @@ public class DomainContent extends EppResource
|
||||
|
||||
public B setDomainName(String domainName) {
|
||||
checkArgument(
|
||||
domainName.equals(canonicalizeDomainName(domainName)),
|
||||
domainName.equals(canonicalizeHostname(domainName)),
|
||||
"Domain name %s not in puny-coded, lower-case form",
|
||||
domainName);
|
||||
getInstance().fullyQualifiedDomainName = domainName;
|
||||
|
||||
@@ -309,7 +309,7 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
|
||||
if (domainContent == null) {
|
||||
domainContent = jpaTm().getEntityManager().find(DomainBase.class, getDomainRepoId());
|
||||
domainContent.fullyQualifiedDomainName =
|
||||
DomainNameUtils.canonicalizeDomainName(domainContent.fullyQualifiedDomainName);
|
||||
DomainNameUtils.canonicalizeHostname(domainContent.fullyQualifiedDomainName);
|
||||
fillAuxiliaryFieldsFromDomain(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import google.registry.model.BackupGroupRoot;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.common.TimedTransitionProperty;
|
||||
import google.registry.model.common.TimedTransitionProperty.TimeMapper;
|
||||
import google.registry.model.common.TimedTransitionProperty.TimedTransition;
|
||||
@@ -151,6 +152,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable, Datas
|
||||
@Enumerated(EnumType.STRING)
|
||||
TokenType tokenType;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "renewalPriceBehavior", nullable = false)
|
||||
RenewalPriceBehavior renewalPriceBehavior = RenewalPriceBehavior.DEFAULT;
|
||||
|
||||
// TODO: Remove onLoad once all allocation tokens are migrated to have a discountYears of 1.
|
||||
@OnLoad
|
||||
void onLoad() {
|
||||
@@ -240,6 +245,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable, Datas
|
||||
return tokenStatusTransitions;
|
||||
}
|
||||
|
||||
public RenewalPriceBehavior getRenewalPriceBehavior() {
|
||||
return renewalPriceBehavior;
|
||||
}
|
||||
|
||||
public VKey<AllocationToken> createVKey() {
|
||||
return VKey.create(AllocationToken.class, getToken(), Key.create(this));
|
||||
}
|
||||
@@ -362,5 +371,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable, Datas
|
||||
"tokenStatusTransitions must start with NOT_STARTED");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRenewalPriceBehavior(RenewalPriceBehavior renewalPriceBehavior) {
|
||||
getInstance().renewalPriceBehavior = renewalPriceBehavior;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import static com.google.common.collect.Sets.difference;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.googlecode.objectify.annotation.IgnoreSave;
|
||||
@@ -195,7 +195,7 @@ public class HostBase extends EppResource {
|
||||
|
||||
public B setHostName(String hostName) {
|
||||
checkArgument(
|
||||
hostName.equals(canonicalizeDomainName(hostName)),
|
||||
hostName.equals(canonicalizeHostname(hostName)),
|
||||
"Host name %s not in puny-coded, lower-case form",
|
||||
hostName);
|
||||
getInstance().fullyQualifiedHostName = hostName;
|
||||
|
||||
@@ -143,7 +143,7 @@ public class HostHistory extends HistoryEntry implements SqlEntity, UnsafeSerial
|
||||
if (hostBase == null) {
|
||||
hostBase = jpaTm().getEntityManager().find(HostResource.class, getHostRepoId());
|
||||
hostBase.fullyQualifiedHostName =
|
||||
DomainNameUtils.canonicalizeDomainName(hostBase.fullyQualifiedHostName);
|
||||
DomainNameUtils.canonicalizeHostname(hostBase.fullyQualifiedHostName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ public class HostResource extends HostBase
|
||||
|
||||
@Override
|
||||
public void beforeSqlSaveOnReplay() {
|
||||
fullyQualifiedHostName = DomainNameUtils.canonicalizeDomainName(fullyQualifiedHostName);
|
||||
fullyQualifiedHostName = DomainNameUtils.canonicalizeHostname(fullyQualifiedHostName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -69,7 +69,7 @@ public class CommitLogMutation extends ImmutableObject implements DatastoreOnlyE
|
||||
* converted to a raw Datastore Entity, serialized to bytes, and stored within the mutation.
|
||||
*/
|
||||
public static CommitLogMutation create(Key<CommitLogManifest> parent, Object entity) {
|
||||
return createFromRaw(parent, auditedOfy().save().toEntity(entity));
|
||||
return createFromRaw(parent, auditedOfy().saveIgnoringReadOnlyWithBackup().toEntity(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -156,7 +156,7 @@ public class CommitLoggedWork<R> implements Runnable {
|
||||
.map(entity -> (ImmutableObject) CommitLogMutation.create(manifestKey, entity))
|
||||
.collect(toImmutableSet());
|
||||
auditedOfy()
|
||||
.saveWithoutBackup()
|
||||
.saveIgnoringReadOnlyWithoutBackup()
|
||||
.entities(
|
||||
new ImmutableSet.Builder<>()
|
||||
.add(manifest)
|
||||
|
||||
@@ -264,8 +264,7 @@ public class Registry extends ImmutableObject
|
||||
/** A cache that loads the {@link Registry} for a given tld. */
|
||||
private static final LoadingCache<String, Optional<Registry>> CACHE =
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(
|
||||
java.time.Duration.ofMillis(getSingletonCacheRefreshDuration().getMillis()))
|
||||
.expireAfterWrite(getSingletonCacheRefreshDuration())
|
||||
.build(
|
||||
new CacheLoader<String, Optional<Registry>>() {
|
||||
@Override
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.model.tld.label;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
@@ -77,7 +77,7 @@ public abstract class DomainLabelEntry<T extends Comparable<?>, D extends Domain
|
||||
public T build() {
|
||||
checkArgumentNotNull(emptyToNull(getInstance().domainLabel), "Label must be specified");
|
||||
checkArgument(
|
||||
getInstance().domainLabel.equals(canonicalizeDomainName(getInstance().domainLabel)),
|
||||
getInstance().domainLabel.equals(canonicalizeHostname(getInstance().domainLabel)),
|
||||
"Label '%s' must be in puny-coded, lower-case form",
|
||||
getInstance().domainLabel);
|
||||
checkArgumentNotNull(getInstance().getValue(), "Value must be specified");
|
||||
|
||||
@@ -31,6 +31,7 @@ import google.registry.batch.ExpandRecurringBillingEventsAction;
|
||||
import google.registry.batch.RefreshDnsOnHostRenameAction;
|
||||
import google.registry.batch.RelockDomainAction;
|
||||
import google.registry.batch.ResaveAllEppResourcesAction;
|
||||
import google.registry.batch.ResaveAllEppResourcesPipelineAction;
|
||||
import google.registry.batch.ResaveEntityAction;
|
||||
import google.registry.batch.SendExpiringCertificateNotificationEmailAction;
|
||||
import google.registry.batch.WipeOutCloudSqlAction;
|
||||
@@ -196,6 +197,8 @@ interface BackendRequestComponent {
|
||||
|
||||
ResaveAllEppResourcesAction resaveAllEppResourcesAction();
|
||||
|
||||
ResaveAllEppResourcesPipelineAction resaveAllEppResourcesPipelineAction();
|
||||
|
||||
ResaveEntityAction resaveEntityAction();
|
||||
|
||||
SendExpiringCertificateNotificationEmailAction sendExpiringCertificateNotificationEmailAction();
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.Nullable;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
@@ -116,20 +117,28 @@ public class JodaMoneyType implements CompositeUserType {
|
||||
return Objects.hashCode(x);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object nullSafeGet(
|
||||
ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
|
||||
throws HibernateException, SQLException {
|
||||
BigDecimal amount = StandardBasicTypes.BIG_DECIMAL.nullSafeGet(rs, names[AMOUNT_ID], session);
|
||||
CurrencyUnit currencyUnit =
|
||||
CurrencyUnit.of(StandardBasicTypes.STRING.nullSafeGet(rs, names[CURRENCY_ID], session));
|
||||
if (amount != null && currencyUnit != null) {
|
||||
return Money.of(currencyUnit, amount.stripTrailingZeros());
|
||||
}
|
||||
if (amount == null && currencyUnit == null) {
|
||||
String currencyUnitString =
|
||||
StandardBasicTypes.STRING.nullSafeGet(rs, names[CURRENCY_ID], session);
|
||||
// It is allowable for a Money object to be null, but only if both the currency unit and the
|
||||
// amount are null
|
||||
if (amount == null && currencyUnitString == null) {
|
||||
return null;
|
||||
} else if (amount != null && currencyUnitString != null) {
|
||||
// CurrencyUnit.of() throws an IllegalCurrencyException for unknown currency, which means the
|
||||
// currency is valid if it returns a value
|
||||
return Money.of(CurrencyUnit.of(currencyUnitString), amount.stripTrailingZeros());
|
||||
} else {
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
"Mismatching null state between currency '%s' and amount '%s'",
|
||||
currencyUnitString, amount));
|
||||
}
|
||||
throw new HibernateException("Mismatching null state between currency and amount.");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -140,7 +149,7 @@ public class JodaMoneyType implements CompositeUserType {
|
||||
String currencyUnit = value == null ? null : ((Money) value).getCurrencyUnit().getCode();
|
||||
|
||||
if ((amount == null && currencyUnit != null) || (amount != null && currencyUnit == null)) {
|
||||
throw new HibernateException("Mismatching null state between currency and amount.");
|
||||
throw new HibernateException("Mismatching null state between currency and amount");
|
||||
}
|
||||
StandardBasicTypes.BIG_DECIMAL.nullSafeSet(st, amount, index, session);
|
||||
StandardBasicTypes.STRING.nullSafeSet(st, currencyUnit, index + 1, session);
|
||||
|
||||
@@ -15,23 +15,25 @@
|
||||
package google.registry.persistence.transaction;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.DATASTORE_PRIMARY_NO_ASYNC;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.appengine.api.utils.SystemProperty;
|
||||
import com.google.appengine.api.utils.SystemProperty.Environment.Value;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Suppliers;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.annotations.DeleteAfterMigration;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.PrimaryDatabase;
|
||||
import google.registry.model.ofy.DatastoreTransactionManager;
|
||||
import google.registry.persistence.DaggerPersistenceComponent;
|
||||
import google.registry.tools.RegistryToolEnvironment;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import google.registry.util.SystemClock;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Factory class to create {@link TransactionManager} instance. */
|
||||
// TODO: Rename this to PersistenceFactory and move to persistence package.
|
||||
@@ -42,6 +44,9 @@ public final class TransactionManagerFactory {
|
||||
/** Optional override to manually set the transaction manager per-test. */
|
||||
private static Optional<TransactionManager> tmForTest = Optional.empty();
|
||||
|
||||
/** The current clock (defined as a variable so we can override it in tests) */
|
||||
private static Clock clock = new SystemClock();
|
||||
|
||||
/** Supplier for jpaTm so that it is initialized only once, upon first usage. */
|
||||
@NonFinalForTesting
|
||||
private static Supplier<JpaTransactionManager> jpaTm =
|
||||
@@ -103,7 +108,7 @@ public final class TransactionManagerFactory {
|
||||
if (onBeam) {
|
||||
return jpaTm();
|
||||
}
|
||||
return DatabaseMigrationStateSchedule.getValueAtTime(DateTime.now(UTC))
|
||||
return DatabaseMigrationStateSchedule.getValueAtTime(clock.nowUtc())
|
||||
.getPrimaryDatabase()
|
||||
.equals(PrimaryDatabase.DATASTORE)
|
||||
? ofyTm()
|
||||
@@ -193,11 +198,33 @@ public final class TransactionManagerFactory {
|
||||
}
|
||||
|
||||
public static void assertNotReadOnlyMode() {
|
||||
if (DatabaseMigrationStateSchedule.getValueAtTime(DateTime.now(UTC)).isReadOnly()) {
|
||||
if (DatabaseMigrationStateSchedule.getValueAtTime(clock.nowUtc()).isReadOnly()) {
|
||||
throw new ReadOnlyModeException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that async actions (contact/host deletes and host renames) are allowed.
|
||||
*
|
||||
* <p>These are allowed at all times except during the {@link
|
||||
* DatabaseMigrationStateSchedule.MigrationState#DATASTORE_PRIMARY_NO_ASYNC} stage. Note that
|
||||
* {@link ReadOnlyModeException} may well be thrown during other read-only stages inside the
|
||||
* transaction manager; this method specifically checks only async actions.
|
||||
*/
|
||||
@DeleteAfterMigration
|
||||
public static void assertAsyncActionsAreAllowed() {
|
||||
if (DatabaseMigrationStateSchedule.getValueAtTime(clock.nowUtc())
|
||||
.equals(DATASTORE_PRIMARY_NO_ASYNC)) {
|
||||
throw new ReadOnlyModeException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Allows us to set the clock used by the factory in unit tests. */
|
||||
@VisibleForTesting
|
||||
public static void setClockForTesting(Clock clock) {
|
||||
TransactionManagerFactory.clock = clock;
|
||||
}
|
||||
|
||||
/** Registry is currently undergoing maintenance and is in read-only mode. */
|
||||
public static class ReadOnlyModeException extends IllegalStateException {
|
||||
public ReadOnlyModeException() {
|
||||
|
||||
@@ -18,7 +18,7 @@ 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.request.Actions.getPathForAction;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
@@ -245,7 +245,7 @@ public abstract class RdapActionBase implements Runnable {
|
||||
}
|
||||
|
||||
String canonicalizeName(String name) {
|
||||
name = canonicalizeDomainName(name);
|
||||
name = canonicalizeHostname(name);
|
||||
if (name.endsWith(".")) {
|
||||
name = name.substring(0, name.length() - 1);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import static com.google.appengine.api.urlfetch.HTTPMethod.PUT;
|
||||
import static com.google.common.io.BaseEncoding.base64;
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
@@ -123,7 +123,7 @@ public class RdeReporter {
|
||||
|
||||
private URL makeReportUrl(String tld, String id) {
|
||||
try {
|
||||
return new URL(String.format("%s/%s/%s", reportUrlPrefix, canonicalizeDomainName(tld), id));
|
||||
return new URL(String.format("%s/%s/%s", reportUrlPrefix, canonicalizeHostname(tld), id));
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
|
||||
// If a prefix is not provided, but we are in SQL mode, try to determine the prefix. This should
|
||||
// only happen when the RDE upload cron job runs to catch up any un-retried (i. e. expected)
|
||||
// RDE failures.
|
||||
if (prefix.isEmpty() && !tm().isOfy()) {
|
||||
if (!prefix.isPresent() && !tm().isOfy()) {
|
||||
// The prefix is always in the format of: rde-2022-02-21t00-00-00z-2022-02-21t00-07-33z, where
|
||||
// the first datetime is the watermark and the second one is the time when the RDE beam job
|
||||
// launched. We search for the latest folder that starts with "rde-[watermark]".
|
||||
|
||||
@@ -17,11 +17,11 @@ package google.registry.tmch;
|
||||
import static google.registry.config.RegistryConfig.ConfigModule.TmchCaMode.PILOT;
|
||||
import static google.registry.config.RegistryConfig.ConfigModule.TmchCaMode.PRODUCTION;
|
||||
import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration;
|
||||
import static google.registry.model.CacheUtils.newCacheBuilder;
|
||||
import static google.registry.util.ResourceUtils.readResourceUtf8;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryConfig.ConfigModule.TmchCaMode;
|
||||
@@ -76,9 +76,7 @@ public final class TmchCertificateAuthority {
|
||||
* persist the correct one for this given environment.
|
||||
*/
|
||||
private static final LoadingCache<TmchCaMode, X509CRL> CRL_CACHE =
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(
|
||||
java.time.Duration.ofMillis(getSingletonCacheRefreshDuration().getMillis()))
|
||||
newCacheBuilder(getSingletonCacheRefreshDuration())
|
||||
.build(
|
||||
new CacheLoader<TmchCaMode, X509CRL>() {
|
||||
@Override
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
@@ -80,7 +80,7 @@ final class CanonicalizeLabelsCommand implements Command {
|
||||
|
||||
private String canonicalize(String rawLabel) {
|
||||
try {
|
||||
return canonicalizeDomainName(rawLabel.replaceAll(" ", ""));
|
||||
return canonicalizeHostname(rawLabel.replaceAll(" ", ""));
|
||||
} catch (Exception e) {
|
||||
System.err.printf("Error canonicalizing %s: %s\n", rawLabel, e.getMessage());
|
||||
return "";
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.util.DomainNameUtils.ACE_PREFIX;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
@@ -38,7 +38,7 @@ final class ConvertIdnCommand implements Command {
|
||||
if (label.startsWith(ACE_PREFIX)) {
|
||||
System.out.println(Idn.toUnicode(Ascii.toLowerCase(label)));
|
||||
} else {
|
||||
System.out.println(canonicalizeDomainName(label));
|
||||
System.out.println(canonicalizeHostname(label));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.net.InetAddresses;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
import google.registry.tools.soy.HostCreateSoyInfo;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
@@ -68,7 +69,7 @@ final class CreateHostCommand extends MutatingEppToolCommand {
|
||||
addSoyRecord(
|
||||
clientId,
|
||||
new SoyMapData(
|
||||
"hostname", hostName,
|
||||
"hostname", DomainNameUtils.canonicalizeHostname(hostName),
|
||||
"ipv4addresses", ipv4Addresses.build(),
|
||||
"ipv6addresses", ipv6Addresses.build()));
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Predicates.isNull;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static google.registry.util.RegistrarUtils.normalizeRegistrarName;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
@@ -319,7 +319,7 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
|
||||
addAllowedTlds.isEmpty(), "Can't specify both --allowedTlds and --addAllowedTlds");
|
||||
ImmutableSet.Builder<String> allowedTldsBuilder = new ImmutableSet.Builder<>();
|
||||
for (String allowedTld : allowedTlds) {
|
||||
allowedTldsBuilder.add(canonicalizeDomainName(allowedTld));
|
||||
allowedTldsBuilder.add(canonicalizeHostname(allowedTld));
|
||||
}
|
||||
builder.setAllowedTlds(allowedTldsBuilder.build());
|
||||
}
|
||||
@@ -329,7 +329,7 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
|
||||
allowedTldsBuilder.addAll(oldRegistrar.getAllowedTlds());
|
||||
}
|
||||
for (String allowedTld : addAllowedTlds) {
|
||||
allowedTldsBuilder.add(canonicalizeDomainName(allowedTld));
|
||||
allowedTldsBuilder.add(canonicalizeHostname(allowedTld));
|
||||
}
|
||||
builder.setAllowedTlds(allowedTldsBuilder.build());
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.util.CollectionUtils.findDuplicates;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.google.common.base.Joiner;
|
||||
@@ -263,10 +263,10 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand {
|
||||
"Can't update roid suffixes on multiple TLDs simultaneously");
|
||||
for (String tld : tlds) {
|
||||
checkArgument(
|
||||
tld.equals(canonicalizeDomainName(tld)),
|
||||
tld.equals(canonicalizeHostname(tld)),
|
||||
"TLD '%s' should be given in the canonical form '%s'",
|
||||
tld,
|
||||
canonicalizeDomainName(tld));
|
||||
canonicalizeHostname(tld));
|
||||
checkArgument(
|
||||
!Character.isDigit(tld.charAt(0)),
|
||||
"TLDs cannot begin with a number");
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
import google.registry.tools.soy.HostDeleteSoyInfo;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
|
||||
/** A command to delete a host via EPP. */
|
||||
@Parameters(separators = " =", commandDescription = "Delete host")
|
||||
@@ -50,9 +51,11 @@ final class DeleteHostCommand extends MutatingEppToolCommand {
|
||||
@Override
|
||||
protected void initMutatingEppToolCommand() {
|
||||
setSoyTemplate(HostDeleteSoyInfo.getInstance(), HostDeleteSoyInfo.DELETEHOST);
|
||||
addSoyRecord(clientId, new SoyMapData(
|
||||
"hostName", hostName,
|
||||
"reason", reason,
|
||||
"requestedByRegistrar", requestedByRegistrar));
|
||||
addSoyRecord(
|
||||
clientId,
|
||||
new SoyMapData(
|
||||
"hostName", DomainNameUtils.canonicalizeHostname(hostName),
|
||||
"reason", reason,
|
||||
"requestedByRegistrar", requestedByRegistrar));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
@@ -61,6 +61,6 @@ class EncryptEscrowDepositCommand implements CommandWithRemoteApi {
|
||||
|
||||
@Override
|
||||
public final void run() throws Exception {
|
||||
encryptor.encrypt(mode, canonicalizeDomainName(tld), revision, input, outdir);
|
||||
encryptor.encrypt(mode, canonicalizeHostname(tld), revision, input, outdir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
@@ -79,6 +80,7 @@ class EnqueuePollMessageCommand extends MutatingCommand {
|
||||
|
||||
@Override
|
||||
protected final void init() {
|
||||
domainName = DomainNameUtils.canonicalizeHostname(domainName);
|
||||
checkArgument(
|
||||
!sendToAll || isNullOrEmpty(clientIds), "Cannot specify both --all and --clients");
|
||||
tm().transact(
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.google.common.collect.Maps.filterValues;
|
||||
import static com.google.common.io.Resources.getResource;
|
||||
import static google.registry.model.tld.Registries.findTldForNameOrThrow;
|
||||
import static google.registry.tools.CommandUtilities.addHeader;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
import static google.registry.xml.XmlTransformer.prettyPrint;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
@@ -83,8 +84,9 @@ abstract class EppToolCommand extends ConfirmingCommand
|
||||
protected static Multimap<String, String> validateAndGroupDomainNamesByTld(List<String> names) {
|
||||
ImmutableMultimap.Builder<String, String> builder = new ImmutableMultimap.Builder<>();
|
||||
for (String name : names) {
|
||||
InternetDomainName tld = findTldForNameOrThrow(InternetDomainName.from(name));
|
||||
builder.put(tld.toString(), name);
|
||||
String canonicalDomain = canonicalizeHostname(name);
|
||||
InternetDomainName tld = findTldForNameOrThrow(InternetDomainName.from(canonicalDomain));
|
||||
builder.put(tld.toString(), canonicalDomain);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Queues.newArrayDeque;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||
@@ -44,15 +43,18 @@ import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.tools.params.TransitionListParameter.TokenStatusTransitions;
|
||||
import google.registry.util.CollectionUtils;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.util.StringGenerator;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collection;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
@@ -164,11 +166,12 @@ class GenerateAllocationTokensCommand implements CommandWithRemoteApi {
|
||||
domainNames = null;
|
||||
} else {
|
||||
domainNames =
|
||||
newArrayDeque(
|
||||
Splitter.on('\n')
|
||||
.omitEmptyStrings()
|
||||
.trimResults()
|
||||
.split(Files.asCharSource(new File(domainNamesFile), UTF_8).read()));
|
||||
Splitter.on('\n')
|
||||
.omitEmptyStrings()
|
||||
.trimResults()
|
||||
.splitToStream(Files.asCharSource(new File(domainNamesFile), UTF_8).read())
|
||||
.map(DomainNameUtils::canonicalizeHostname)
|
||||
.collect(Collectors.toCollection(ArrayDeque::new));
|
||||
numTokens = domainNames.size();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import java.util.List;
|
||||
|
||||
/** Command to show a domain resource. */
|
||||
@@ -33,8 +34,11 @@ final class GetDomainCommand extends GetEppResourceCommand {
|
||||
@Override
|
||||
public void runAndPrint() {
|
||||
for (String domainName : mainParameters) {
|
||||
String canonicalDomain = DomainNameUtils.canonicalizeHostname(domainName);
|
||||
printResource(
|
||||
"Domain", domainName, loadByForeignKey(DomainBase.class, domainName, readTimestamp));
|
||||
"Domain",
|
||||
canonicalDomain,
|
||||
loadByForeignKey(DomainBase.class, canonicalDomain, readTimestamp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import java.util.List;
|
||||
|
||||
/** Command to show one or more host resources. */
|
||||
@@ -32,7 +33,9 @@ final class GetHostCommand extends GetEppResourceCommand {
|
||||
|
||||
@Override
|
||||
public void runAndPrint() {
|
||||
mainParameters.forEach(
|
||||
h -> printResource("Host", h, loadByForeignKey(HostResource.class, h, readTimestamp)));
|
||||
mainParameters.stream()
|
||||
.map(DomainNameUtils::canonicalizeHostname)
|
||||
.forEach(
|
||||
h -> printResource("Host", h, loadByForeignKey(HostResource.class, h, readTimestamp)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
/**
|
||||
@@ -26,6 +28,6 @@ public class LockDomainCommand extends LockOrUnlockDomainCommand {
|
||||
|
||||
@Override
|
||||
protected void createAndApplyRequest(String domain) {
|
||||
domainLockUtils.administrativelyApplyLock(domain, clientId, null, true);
|
||||
domainLockUtils.administrativelyApplyLock(canonicalizeHostname(domain), clientId, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,9 @@ import java.io.ByteArrayInputStream;
|
||||
import java.net.URL;
|
||||
import java.security.Security;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.postgresql.util.PSQLException;
|
||||
|
||||
/** Container class to create and run remote commands against a Datastore instance. */
|
||||
@Parameters(separators = " =", commandDescription = "Command-line interface to the registry")
|
||||
@@ -178,14 +180,30 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||
|
||||
try {
|
||||
runCommand(command);
|
||||
} catch (RuntimeException ex) {
|
||||
if (Throwables.getRootCause(ex) instanceof LoginRequiredException) {
|
||||
} catch (RuntimeException e) {
|
||||
if (Throwables.getRootCause(e) instanceof LoginRequiredException) {
|
||||
System.err.println("===================================================================");
|
||||
System.err.println("You must login using 'nomulus login' prior to running this command.");
|
||||
System.err.println("===================================================================");
|
||||
System.exit(1);
|
||||
} else {
|
||||
throw ex;
|
||||
// See if this looks like the error we get when there's another instance of nomulus tool
|
||||
// running against SQL and give the user some additional guidance if so.
|
||||
Optional<Throwable> psqlException =
|
||||
Throwables.getCausalChain(e).stream()
|
||||
.filter(x -> x instanceof PSQLException)
|
||||
.findFirst();
|
||||
if (psqlException.isPresent() && psqlException.get().getMessage().contains("google:5432")) {
|
||||
e.printStackTrace();
|
||||
System.err.println("===================================================================");
|
||||
System.err.println(
|
||||
"This error is likely the result of having another instance of\n"
|
||||
+ "nomulus running at the same time. Check your system, shut down\n"
|
||||
+ "the other instance, and try again.");
|
||||
System.err.println("===================================================================");
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.tools.soy.DomainRenewSoyInfo;
|
||||
import google.registry.tools.soy.UniformRapidSuspensionSoyInfo;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -122,12 +123,14 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
|
||||
protected void initMutatingEppToolCommand() {
|
||||
superuser = true;
|
||||
DateTime now = DateTime.now(UTC);
|
||||
ImmutableSet<String> newHostsSet = ImmutableSet.copyOf(newHosts);
|
||||
ImmutableList<String> newCanonicalHosts =
|
||||
newHosts.stream().map(DomainNameUtils::canonicalizeHostname).collect(toImmutableList());
|
||||
ImmutableSet<String> newHostsSet = ImmutableSet.copyOf(newCanonicalHosts);
|
||||
Optional<DomainBase> domainOpt = loadByForeignKey(DomainBase.class, domainName, now);
|
||||
checkArgumentPresent(domainOpt, "Domain '%s' does not exist or is deleted", domainName);
|
||||
DomainBase domain = domainOpt.get();
|
||||
Set<String> missingHosts =
|
||||
difference(newHostsSet, checkResourcesExist(HostResource.class, newHosts, now));
|
||||
difference(newHostsSet, checkResourcesExist(HostResource.class, newCanonicalHosts, now));
|
||||
checkArgument(missingHosts.isEmpty(), "Hosts do not exist: %s", missingHosts);
|
||||
checkArgument(
|
||||
locksToPreserve.isEmpty() || undo,
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -27,6 +29,7 @@ public class UnlockDomainCommand extends LockOrUnlockDomainCommand {
|
||||
|
||||
@Override
|
||||
protected void createAndApplyRequest(String domain) {
|
||||
domainLockUtils.administrativelyApplyUnlock(domain, clientId, true, Optional.empty());
|
||||
domainLockUtils.administrativelyApplyUnlock(
|
||||
canonicalizeHostname(domain), clientId, true, Optional.empty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.tools.params;
|
||||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
|
||||
/** InternetDomainName CLI parameter converter/validator. */
|
||||
public final class InternetDomainNameParameter
|
||||
@@ -26,6 +27,6 @@ public final class InternetDomainNameParameter
|
||||
|
||||
@Override
|
||||
public InternetDomainName convert(String value) {
|
||||
return InternetDomainName.from(value);
|
||||
return InternetDomainName.from(DomainNameUtils.canonicalizeHostname(value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ package google.registry.ui.server;
|
||||
import static com.google.common.collect.Range.atLeast;
|
||||
import static com.google.common.collect.Range.atMost;
|
||||
import static com.google.common.collect.Range.closed;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Splitter;
|
||||
@@ -334,7 +334,7 @@ public final class RegistrarFormFields {
|
||||
if (!InternetDomainName.isValid(input)) {
|
||||
throw new FormFieldException("Not a valid hostname.");
|
||||
}
|
||||
return canonicalizeDomainName(input);
|
||||
return canonicalizeHostname(input);
|
||||
}
|
||||
|
||||
public static @Nullable DateTime parseDateTime(@Nullable String input) {
|
||||
|
||||
@@ -17,7 +17,7 @@ package google.registry.whois;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.model.tld.Registries.findTldForName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
@@ -122,7 +122,7 @@ class WhoisReader {
|
||||
logger.atInfo().log(
|
||||
"Attempting domain lookup command using domain name '%s'.", tokens.get(1));
|
||||
return commandFactory.domainLookup(
|
||||
InternetDomainName.from(canonicalizeDomainName(tokens.get(1))),
|
||||
InternetDomainName.from(canonicalizeHostname(tokens.get(1))),
|
||||
fullOutput,
|
||||
whoisRedactedEmailText);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
@@ -152,8 +152,8 @@ class WhoisReader {
|
||||
try {
|
||||
logger.atInfo().log(
|
||||
"Attempting nameserver lookup command using %s as a hostname.", tokens.get(1));
|
||||
return commandFactory.nameserverLookupByHost(InternetDomainName.from(
|
||||
canonicalizeDomainName(tokens.get(1))));
|
||||
return commandFactory.nameserverLookupByHost(
|
||||
InternetDomainName.from(canonicalizeHostname(tokens.get(1))));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// Silently ignore this exception.
|
||||
}
|
||||
@@ -187,7 +187,7 @@ class WhoisReader {
|
||||
|
||||
// Try to parse it as a domain name or host name.
|
||||
try {
|
||||
final InternetDomainName targetName = InternetDomainName.from(canonicalizeDomainName(arg1));
|
||||
final InternetDomainName targetName = InternetDomainName.from(canonicalizeHostname(arg1));
|
||||
|
||||
// We don't know at this point whether we have a domain name or a host name. We have to
|
||||
// search through our configured TLDs to see if there's one that prefixes the name.
|
||||
|
||||
@@ -31,7 +31,7 @@ JOIN Domain d ON b.domainRepoId = d.repoId
|
||||
JOIN Tld t ON t.tldStrId = d.tld
|
||||
LEFT JOIN BillingCancellation c ON b.id = c.refOneTime.billingId
|
||||
LEFT JOIN BillingCancellation cr ON b.cancellationMatchingBillingEvent = cr.refRecurring.billingId
|
||||
WHERE r.billingIdentifier IS NOT NULL
|
||||
WHERE r.billingAccountMap IS NOT NULL
|
||||
AND r.type = 'REAL'
|
||||
AND t.invoicingEnabled IS TRUE
|
||||
AND b.billingTime BETWEEN CAST('%FIRST_TIMESTAMP_OF_MONTH%' AS timestamp) AND CAST('%LAST_TIMESTAMP_OF_MONTH%' AS timestamp)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "Resave all EPP Resources",
|
||||
"description": "An Apache Beam pipeline that resaves all (or potentially only changed) EPP resources",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "registryEnvironment",
|
||||
"label": "The Registry environment.",
|
||||
"helpText": "The Registry environment.",
|
||||
"is_optional": false,
|
||||
"regexes": [
|
||||
"^PRODUCTION|SANDBOX|CRASH|QA|ALPHA$"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "isolationOverride",
|
||||
"label": "The desired SQL transaction isolation level.",
|
||||
"helpText": "The desired SQL transaction isolation level.",
|
||||
"is_optional": true,
|
||||
"regexes": [
|
||||
"^[0-9A-Z_]+$"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "fast",
|
||||
"label": "Whether or not to attempt to only save changed resources",
|
||||
"helpText": "If true, we will attempt to only save resources that possibly have expired transfers, grace periods, etc",
|
||||
"is_optional": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -92,7 +92,7 @@ public class DeleteOldCommitLogsActionTest
|
||||
contact = auditedOfy().load().type(ContactResource.class).first().now();
|
||||
|
||||
// The following value might change if {@link CommitLogRevisionsTranslatorFactory} changes.
|
||||
assertThat(contact.getRevisions().size()).isEqualTo(6);
|
||||
assertThat(contact.getRevisions()).hasSize(6);
|
||||
|
||||
// Before deleting the unneeded manifests - we have 11 of them (one for the first
|
||||
// creation, and 10 more for the mutateContacts)
|
||||
|
||||
@@ -20,6 +20,7 @@ import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistContactWithPendingTransfer;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import google.registry.model.annotations.DeleteAfterMigration;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.testing.FakeResponse;
|
||||
@@ -29,6 +30,9 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link ResaveAllEppResourcesAction}. */
|
||||
// No longer needed in SQL. Subject to future removal.
|
||||
@Deprecated
|
||||
@DeleteAfterMigration
|
||||
class ResaveAllEppResourcesActionTest extends MapreduceTestCase<ResaveAllEppResourcesAction> {
|
||||
|
||||
@BeforeEach
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.batch;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.batch.ResaveAllEppResourcesPipelineAction.PARAM_FAST;
|
||||
import static google.registry.batch.ResaveAllEppResourcesPipelineAction.PIPELINE_NAME;
|
||||
import static google.registry.beam.BeamUtils.createJobName;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import com.google.api.services.dataflow.model.LaunchFlexTemplateParameter;
|
||||
import com.google.api.services.dataflow.model.LaunchFlexTemplateRequest;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.beam.BeamActionTestBase;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link ResaveAllEppResourcesPipelineAction}. */
|
||||
public class ResaveAllEppResourcesPipelineActionTest extends BeamActionTestBase {
|
||||
|
||||
@RegisterExtension
|
||||
final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withDatastoreAndCloudSql().build();
|
||||
|
||||
private final FakeClock fakeClock = new FakeClock();
|
||||
|
||||
private ResaveAllEppResourcesPipelineAction createAction(boolean isFast) {
|
||||
return new ResaveAllEppResourcesPipelineAction(
|
||||
"test-project",
|
||||
"test-region",
|
||||
"staging-bucket",
|
||||
Optional.of(isFast),
|
||||
fakeClock,
|
||||
response,
|
||||
dataflow);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLaunch_notFast() throws Exception {
|
||||
createAction(false).run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(response.getPayload()).isEqualTo("Launched resaveAllEppResources pipeline: jobid");
|
||||
verify(templates).launch("test-project", "test-region", createLaunchTemplateRequest(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLaunch_fast() throws Exception {
|
||||
createAction(true).run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(response.getPayload()).isEqualTo("Launched resaveAllEppResources pipeline: jobid");
|
||||
verify(templates).launch("test-project", "test-region", createLaunchTemplateRequest(true));
|
||||
}
|
||||
|
||||
private LaunchFlexTemplateRequest createLaunchTemplateRequest(boolean isFast) {
|
||||
return new LaunchFlexTemplateRequest()
|
||||
.setLaunchParameter(
|
||||
new LaunchFlexTemplateParameter()
|
||||
.setJobName(createJobName("resave-all-epp-resources", fakeClock))
|
||||
.setContainerSpecGcsPath(
|
||||
String.format("%s/%s_metadata.json", "staging-bucket", PIPELINE_NAME))
|
||||
.setParameters(
|
||||
new ImmutableMap.Builder<String, String>()
|
||||
.put(PARAM_FAST, Boolean.toString(isFast))
|
||||
.put("registryEnvironment", RegistryEnvironment.get().name())
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.batch;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.testing.AppEngineExtension.makeRegistrar1;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
@@ -430,7 +431,7 @@ class SendExpiringCertificateNotificationEmailActionTest {
|
||||
}
|
||||
|
||||
ImmutableList<RegistrarInfo> results = action.getRegistrarsWithExpiringCertificates();
|
||||
assertThat(results.size()).isEqualTo(numOfRegistrarsWithExpiringCertificates);
|
||||
assertThat(results).hasSize(numOfRegistrarsWithExpiringCertificates);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
|
||||
@@ -24,6 +24,10 @@ import static google.registry.testing.DatabaseHelper.persistNewRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.CAD;
|
||||
import static org.joda.money.CurrencyUnit.JPY;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@@ -55,6 +59,7 @@ import java.time.ZonedDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import org.apache.beam.sdk.Pipeline.PipelineExecutionException;
|
||||
import org.apache.beam.sdk.coders.SerializableCoder;
|
||||
import org.apache.beam.sdk.options.PipelineOptionsFactory;
|
||||
import org.apache.beam.sdk.testing.PAssert;
|
||||
@@ -294,6 +299,37 @@ class InvoicingPipelineTest {
|
||||
pipeline.run().waitUntilFinish();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_readFromCloudSqlMissingPAK() throws Exception {
|
||||
Registrar registrar = persistNewRegistrar("TheRegistrar");
|
||||
registrar =
|
||||
registrar
|
||||
.asBuilder()
|
||||
.setBillingAccountMap(ImmutableMap.of(USD, "789"))
|
||||
.setPoNumber(Optional.of("22446688"))
|
||||
.build();
|
||||
persistResource(registrar);
|
||||
Registry test =
|
||||
newRegistry("test", "_TEST", ImmutableSortedMap.of(START_OF_TIME, GENERAL_AVAILABILITY))
|
||||
.asBuilder()
|
||||
.setInvoicingEnabled(true)
|
||||
.build();
|
||||
persistResource(test);
|
||||
DomainBase domain = persistActiveDomain("mycanadiandomain.test");
|
||||
|
||||
persistOneTimeBillingEvent(1, domain, registrar, Reason.RENEW, 3, Money.of(CAD, 20.5));
|
||||
PCollection<BillingEvent> billingEvents = InvoicingPipeline.readFromCloudSql(options, pipeline);
|
||||
billingEvents = billingEvents.apply(new ChangeDomainRepo());
|
||||
PAssert.that(billingEvents).empty();
|
||||
PipelineExecutionException thrown =
|
||||
assertThrows(PipelineExecutionException.class, () -> pipeline.run().waitUntilFinish());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains(
|
||||
"Registrar TheRegistrar does not have a product account key for the currency unit:"
|
||||
+ " CAD");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_saveInvoiceCsv() throws Exception {
|
||||
InvoicingPipeline.saveInvoiceCsv(billingEvents, options);
|
||||
@@ -338,7 +374,7 @@ class InvoicingPipelineTest {
|
||||
+ "LEFT JOIN BillingCancellation c ON b.id = c.refOneTime.billingId\n"
|
||||
+ "LEFT JOIN BillingCancellation cr ON b.cancellationMatchingBillingEvent ="
|
||||
+ " cr.refRecurring.billingId\n"
|
||||
+ "WHERE r.billingIdentifier IS NOT NULL\n"
|
||||
+ "WHERE r.billingAccountMap IS NOT NULL\n"
|
||||
+ "AND r.type = 'REAL'\n"
|
||||
+ "AND t.invoicingEnabled IS TRUE\n"
|
||||
+ "AND b.billingTime BETWEEN CAST('2017-10-01' AS timestamp) AND CAST('2017-11-01'"
|
||||
@@ -362,18 +398,22 @@ class InvoicingPipelineTest {
|
||||
persistNewRegistrar("NewRegistrar");
|
||||
persistNewRegistrar("TheRegistrar");
|
||||
Registrar registrar1 = persistNewRegistrar("theRegistrar");
|
||||
registrar1 = registrar1.asBuilder().setBillingIdentifier(234L).build();
|
||||
registrar1 =
|
||||
registrar1
|
||||
.asBuilder()
|
||||
.setBillingAccountMap(ImmutableMap.of(JPY, "234", USD, "234"))
|
||||
.build();
|
||||
persistResource(registrar1);
|
||||
Registrar registrar2 = persistNewRegistrar("bestdomains");
|
||||
registrar2 =
|
||||
registrar2
|
||||
.asBuilder()
|
||||
.setBillingIdentifier(456L)
|
||||
.setBillingAccountMap(ImmutableMap.of(USD, "456"))
|
||||
.setPoNumber(Optional.of("116688"))
|
||||
.build();
|
||||
persistResource(registrar2);
|
||||
Registrar registrar3 = persistNewRegistrar("anotherRegistrar");
|
||||
registrar3 = registrar3.asBuilder().setBillingIdentifier(789L).build();
|
||||
registrar3 = registrar3.asBuilder().setBillingAccountMap(ImmutableMap.of(USD, "789")).build();
|
||||
persistResource(registrar3);
|
||||
|
||||
Registry test =
|
||||
@@ -397,10 +437,8 @@ class InvoicingPipelineTest {
|
||||
DomainBase domain6 = persistActiveDomain("locked.test");
|
||||
DomainBase domain7 = persistActiveDomain("update-prohibited.test");
|
||||
|
||||
persistOneTimeBillingEvent(
|
||||
1, domain1, registrar1, Reason.RENEW, 3, Money.of(CurrencyUnit.USD, 20.5));
|
||||
persistOneTimeBillingEvent(
|
||||
2, domain2, registrar1, Reason.RENEW, 3, Money.of(CurrencyUnit.USD, 20.5));
|
||||
persistOneTimeBillingEvent(1, domain1, registrar1, Reason.RENEW, 3, Money.of(USD, 20.5));
|
||||
persistOneTimeBillingEvent(2, domain2, registrar1, Reason.RENEW, 3, Money.of(USD, 20.5));
|
||||
persistOneTimeBillingEvent(
|
||||
3,
|
||||
domain3,
|
||||
@@ -410,31 +448,27 @@ class InvoicingPipelineTest {
|
||||
Money.ofMajor(CurrencyUnit.JPY, 70),
|
||||
DateTime.parse("2017-09-29T00:00:00.0Z"),
|
||||
DateTime.parse("2017-10-02T00:00:00.0Z"));
|
||||
persistOneTimeBillingEvent(
|
||||
4, domain4, registrar2, Reason.RENEW, 1, Money.of(CurrencyUnit.USD, 20.5));
|
||||
persistOneTimeBillingEvent(4, domain4, registrar2, Reason.RENEW, 1, Money.of(USD, 20.5));
|
||||
persistOneTimeBillingEvent(
|
||||
5,
|
||||
domain5,
|
||||
registrar3,
|
||||
Reason.CREATE,
|
||||
1,
|
||||
Money.of(CurrencyUnit.USD, 0),
|
||||
Money.of(USD, 0),
|
||||
DateTime.parse("2017-10-04T00:00:00.0Z"),
|
||||
DateTime.parse("2017-10-04T00:00:00.0Z"),
|
||||
Flag.SUNRISE,
|
||||
Flag.ANCHOR_TENANT);
|
||||
persistOneTimeBillingEvent(
|
||||
6, domain6, registrar1, Reason.SERVER_STATUS, 0, Money.of(CurrencyUnit.USD, 0));
|
||||
persistOneTimeBillingEvent(
|
||||
7, domain7, registrar1, Reason.SERVER_STATUS, 0, Money.of(CurrencyUnit.USD, 20));
|
||||
persistOneTimeBillingEvent(6, domain6, registrar1, Reason.SERVER_STATUS, 0, Money.of(USD, 0));
|
||||
persistOneTimeBillingEvent(7, domain7, registrar1, Reason.SERVER_STATUS, 0, Money.of(USD, 20));
|
||||
|
||||
// Add billing event for a non-billable registrar
|
||||
Registrar registrar4 = persistNewRegistrar("noBillRegistrar");
|
||||
registrar4 = registrar4.asBuilder().setBillingIdentifier(null).build();
|
||||
registrar4 = registrar4.asBuilder().setBillingAccountMap(null).build();
|
||||
persistResource(registrar4);
|
||||
DomainBase domain8 = persistActiveDomain("non-billable.test");
|
||||
persistOneTimeBillingEvent(
|
||||
8, domain8, registrar4, Reason.RENEW, 3, Money.of(CurrencyUnit.USD, 20.5));
|
||||
persistOneTimeBillingEvent(8, domain8, registrar4, Reason.RENEW, 3, Money.of(USD, 20.5));
|
||||
|
||||
// Add billing event for a non-real registrar
|
||||
Registrar registrar5 = persistNewRegistrar("notRealRegistrar");
|
||||
@@ -442,19 +476,17 @@ class InvoicingPipelineTest {
|
||||
registrar5
|
||||
.asBuilder()
|
||||
.setIanaIdentifier(null)
|
||||
.setBillingIdentifier(456L)
|
||||
.setBillingAccountMap(ImmutableMap.of(USD, "456"))
|
||||
.setType(Registrar.Type.OTE)
|
||||
.build();
|
||||
persistResource(registrar5);
|
||||
DomainBase domain9 = persistActiveDomain("not-real.test");
|
||||
persistOneTimeBillingEvent(
|
||||
9, domain9, registrar5, Reason.RENEW, 3, Money.of(CurrencyUnit.USD, 20.5));
|
||||
persistOneTimeBillingEvent(9, domain9, registrar5, Reason.RENEW, 3, Money.of(USD, 20.5));
|
||||
|
||||
// Add billing event for a non-invoicing TLD
|
||||
createTld("nobill");
|
||||
DomainBase domain10 = persistActiveDomain("test.nobill");
|
||||
persistOneTimeBillingEvent(
|
||||
10, domain10, registrar1, Reason.RENEW, 3, Money.of(CurrencyUnit.USD, 20.5));
|
||||
persistOneTimeBillingEvent(10, domain10, registrar1, Reason.RENEW, 3, Money.of(USD, 20.5));
|
||||
|
||||
// Add billing event before October 2017
|
||||
DomainBase domain11 = persistActiveDomain("july.test");
|
||||
@@ -471,8 +503,7 @@ class InvoicingPipelineTest {
|
||||
// Add a billing event with a corresponding cancellation
|
||||
DomainBase domain12 = persistActiveDomain("cancel.test");
|
||||
OneTime oneTime =
|
||||
persistOneTimeBillingEvent(
|
||||
12, domain12, registrar1, Reason.RENEW, 3, Money.of(CurrencyUnit.USD, 20.5));
|
||||
persistOneTimeBillingEvent(12, domain12, registrar1, Reason.RENEW, 3, Money.of(USD, 20.5));
|
||||
DomainHistory domainHistory = persistDomainHistory(domain12, registrar1);
|
||||
|
||||
Cancellation cancellation =
|
||||
@@ -507,8 +538,7 @@ class InvoicingPipelineTest {
|
||||
.build();
|
||||
persistResource(recurring);
|
||||
OneTime oneTimeRecurring =
|
||||
persistOneTimeBillingEvent(
|
||||
13, domain13, registrar1, Reason.RENEW, 3, Money.of(CurrencyUnit.USD, 20.5));
|
||||
persistOneTimeBillingEvent(13, domain13, registrar1, Reason.RENEW, 3, Money.of(USD, 20.5));
|
||||
oneTimeRecurring =
|
||||
oneTimeRecurring
|
||||
.asBuilder()
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.beam.resave;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadAllOf;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistContactWithPendingTransfer;
|
||||
import static google.registry.testing.DatabaseHelper.persistDomainWithDependentResources;
|
||||
import static google.registry.testing.DatabaseHelper.persistDomainWithPendingTransfer;
|
||||
import static google.registry.testing.DatabaseHelper.persistNewRegistrars;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import google.registry.beam.TestPipelineExtension;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import google.registry.persistence.transaction.TransactionManagerFactory;
|
||||
import google.registry.testing.DatastoreEntityExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.TmOverrideExtension;
|
||||
import org.apache.beam.sdk.options.PipelineOptionsFactory;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
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;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
/** Tests for {@link ResaveAllEppResourcesPipeline}. */
|
||||
public class ResaveAllEppResourcesPipelineTest {
|
||||
|
||||
private final FakeClock fakeClock = new FakeClock(DateTime.parse("2020-03-10T00:00:00.000Z"));
|
||||
|
||||
@RegisterExtension
|
||||
@Order(Order.DEFAULT - 1)
|
||||
final transient DatastoreEntityExtension datastore =
|
||||
new DatastoreEntityExtension().allThreads(true);
|
||||
|
||||
@RegisterExtension
|
||||
final TestPipelineExtension testPipeline =
|
||||
TestPipelineExtension.create().enableAbandonedNodeEnforcement(true);
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationTestExtension database =
|
||||
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationTestExtension();
|
||||
|
||||
@RegisterExtension
|
||||
@Order(Order.DEFAULT + 1)
|
||||
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withJpa();
|
||||
|
||||
private final ResaveAllEppResourcesPipelineOptions options =
|
||||
PipelineOptionsFactory.create().as(ResaveAllEppResourcesPipelineOptions.class);
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
options.setFast(true);
|
||||
persistNewRegistrars("TheRegistrar", "NewRegistrar");
|
||||
createTld("tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPipeline_unchangedEntity() {
|
||||
ContactResource contact = persistActiveContact("test123");
|
||||
DateTime creationTime = contact.getUpdateTimestamp().getTimestamp();
|
||||
fakeClock.advanceOneMilli();
|
||||
assertThat(loadByEntity(contact).getUpdateTimestamp().getTimestamp()).isEqualTo(creationTime);
|
||||
fakeClock.advanceOneMilli();
|
||||
runPipeline();
|
||||
assertThat(loadByEntity(contact)).isEqualTo(contact);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPipeline_fulfilledContactTransfer() {
|
||||
ContactResource contact = persistActiveContact("test123");
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
contact = persistContactWithPendingTransfer(contact, now, now.plusDays(5), now);
|
||||
fakeClock.advanceBy(Duration.standardDays(10));
|
||||
assertThat(loadByEntity(contact).getStatusValues()).contains(StatusValue.PENDING_TRANSFER);
|
||||
runPipeline();
|
||||
assertThat(loadByEntity(contact).getStatusValues())
|
||||
.doesNotContain(StatusValue.PENDING_TRANSFER);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPipeline_fulfilledDomainTransfer() {
|
||||
options.setFast(true);
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
DomainBase domain =
|
||||
persistDomainWithPendingTransfer(
|
||||
persistDomainWithDependentResources(
|
||||
"domain",
|
||||
"tld",
|
||||
persistActiveContact("jd1234"),
|
||||
now.minusDays(5),
|
||||
now.minusDays(5),
|
||||
now.plusYears(2)),
|
||||
now.minusDays(4),
|
||||
now.minusDays(1),
|
||||
now.plusYears(2));
|
||||
assertThat(domain.getStatusValues()).contains(StatusValue.PENDING_TRANSFER);
|
||||
assertThat(domain.getUpdateTimestamp().getTimestamp()).isEqualTo(now);
|
||||
fakeClock.advanceOneMilli();
|
||||
runPipeline();
|
||||
DomainBase postPipeline = loadByEntity(domain);
|
||||
assertThat(postPipeline.getStatusValues()).doesNotContain(StatusValue.PENDING_TRANSFER);
|
||||
assertThat(postPipeline.getUpdateTimestamp().getTimestamp()).isEqualTo(fakeClock.nowUtc());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPipeline_autorenewedDomain() {
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
DomainBase domain =
|
||||
persistDomainWithDependentResources(
|
||||
"domain", "tld", persistActiveContact("jd1234"), now, now, now.plusYears(1));
|
||||
assertThat(domain.getRegistrationExpirationTime()).isEqualTo(now.plusYears(1));
|
||||
fakeClock.advanceBy(Duration.standardDays(500));
|
||||
runPipeline();
|
||||
DomainBase postPipeline = loadByEntity(domain);
|
||||
assertThat(postPipeline.getRegistrationExpirationTime()).isEqualTo(now.plusYears(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPipeline_expiredGracePeriod() {
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
persistDomainWithDependentResources(
|
||||
"domain", "tld", persistActiveContact("jd1234"), now, now, now.plusYears(1));
|
||||
assertThat(loadAllOf(GracePeriod.class)).hasSize(1);
|
||||
fakeClock.advanceBy(Duration.standardDays(500));
|
||||
runPipeline();
|
||||
assertThat(loadAllOf(GracePeriod.class)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPipeline_fastOnlySavesChanged() {
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
ContactResource contact = persistActiveContact("jd1234");
|
||||
persistDomainWithDependentResources("renewed", "tld", contact, now, now, now.plusYears(1));
|
||||
persistActiveDomain("nonrenewed.tld", now, now.plusYears(20));
|
||||
// Spy the transaction manager so we can be sure we're only saving the renewed domain
|
||||
JpaTransactionManager spy = spy(jpaTm());
|
||||
TransactionManagerFactory.setJpaTm(() -> spy);
|
||||
ArgumentCaptor<DomainBase> domainPutCaptor = ArgumentCaptor.forClass(DomainBase.class);
|
||||
runPipeline();
|
||||
// We should only be attempting to put the one changed domain into the DB
|
||||
verify(spy).put(domainPutCaptor.capture());
|
||||
assertThat(domainPutCaptor.getValue().getDomainName()).isEqualTo("renewed.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPipeline_notFastResavesAll() {
|
||||
options.setFast(false);
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
ContactResource contact = persistActiveContact("jd1234");
|
||||
DomainBase renewed =
|
||||
persistDomainWithDependentResources("renewed", "tld", contact, now, now, now.plusYears(1));
|
||||
DomainBase nonRenewed =
|
||||
persistDomainWithDependentResources(
|
||||
"nonrenewed", "tld", contact, now, now, now.plusYears(20));
|
||||
// Spy the transaction manager so we can be sure we're attempting to save everything
|
||||
JpaTransactionManager spy = spy(jpaTm());
|
||||
TransactionManagerFactory.setJpaTm(() -> spy);
|
||||
ArgumentCaptor<EppResource> eppResourcePutCaptor = ArgumentCaptor.forClass(EppResource.class);
|
||||
runPipeline();
|
||||
// We should be attempting to put both domains (and the contact) in, even the unchanged ones
|
||||
verify(spy, times(3)).put(eppResourcePutCaptor.capture());
|
||||
assertThat(
|
||||
eppResourcePutCaptor.getAllValues().stream()
|
||||
.map(EppResource::getRepoId)
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly(contact.getRepoId(), renewed.getRepoId(), nonRenewed.getRepoId());
|
||||
}
|
||||
|
||||
private void runPipeline() {
|
||||
ResaveAllEppResourcesPipeline pipeline = new ResaveAllEppResourcesPipeline(options);
|
||||
pipeline.setupPipeline(testPipeline);
|
||||
testPipeline.run().waitUntilFinish();
|
||||
}
|
||||
}
|
||||
@@ -282,7 +282,7 @@ class Spec11PipelineTest {
|
||||
ImmutableList.copyOf(
|
||||
ResourceUtils.readResourceUtf8(this.getClass(), "test_output.txt").split("\n"));
|
||||
ImmutableList<String> resultFileContents = resultFileContents();
|
||||
assertThat(resultFileContents.size()).isEqualTo(expectedFileContents.size());
|
||||
assertThat(resultFileContents).hasSize(expectedFileContents.size());
|
||||
assertThat(resultFileContents.get(0)).isEqualTo(expectedFileContents.get(0));
|
||||
assertThat(resultFileContents.subList(1, resultFileContents.size()))
|
||||
.comparingElementsUsing(
|
||||
|
||||
@@ -254,6 +254,15 @@ class ContactDeleteFlowTest extends ResourceFlowTestCase<ContactDeleteFlow, Cont
|
||||
assertIcannReportingActivityFieldLogged("srs-cont-delete");
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testModification_duringNoAsyncPhase() throws Exception {
|
||||
persistActiveContact(getUniqueIdFromCommand());
|
||||
DatabaseHelper.setMigrationScheduleToDatastorePrimaryNoAsync(clock);
|
||||
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
DatabaseHelper.removeDatabaseMigrationSchedule();
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testModification_duringReadOnlyPhase() throws Exception {
|
||||
persistActiveContact(getUniqueIdFromCommand());
|
||||
|
||||
@@ -840,6 +840,15 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
doSuccessfulTest();
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testSuccess_inNoAsyncPhase() throws Exception {
|
||||
DatabaseHelper.setMigrationScheduleToDatastorePrimaryNoAsync(clock);
|
||||
persistContactsAndHosts();
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_create_response_noasync.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
DatabaseHelper.removeDatabaseMigrationSchedule();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_maxNumberOfNameservers() throws Exception {
|
||||
setEppInput("domain_create_13_nameservers.xml");
|
||||
|
||||
@@ -358,6 +358,15 @@ class HostDeleteFlowTest extends ResourceFlowTestCase<HostDeleteFlow, HostResour
|
||||
DatabaseHelper.removeDatabaseMigrationSchedule();
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testModification_duringNoAsyncPhase() {
|
||||
persistActiveHost("ns1.example.tld");
|
||||
DatabaseHelper.setMigrationScheduleToDatastorePrimaryNoAsync(clock);
|
||||
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
DatabaseHelper.removeDatabaseMigrationSchedule();
|
||||
}
|
||||
|
||||
private void assertOfyDeleteSuccess(String registrarId, String clientTrid, boolean isSuperuser)
|
||||
throws Exception {
|
||||
HostResource deletedHost = reloadResourceByForeignKey();
|
||||
|
||||
@@ -54,13 +54,10 @@ class HostInfoFlowTest extends ResourceFlowTestCase<HostInfoFlow, HostResource>
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
HostInfoFlowTest() {
|
||||
setEppInput("host_info.xml", ImmutableMap.of("HOSTNAME", "ns1.example.tld"));
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void initHostTest() {
|
||||
createTld("foobar");
|
||||
setEppInput("host_info.xml", ImmutableMap.of("HOSTNAME", "ns1.example.tld"));
|
||||
}
|
||||
|
||||
private HostResource persistHostResource() throws Exception {
|
||||
|
||||
@@ -1355,7 +1355,43 @@ class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, HostResour
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testModification_duringReadOnlyPhase() throws Exception {
|
||||
void testSuccess_nonHostRename_inNoAsyncPhase_succeeds() throws Exception {
|
||||
setEppInput("host_update_name_unchanged.xml");
|
||||
createTld("tld");
|
||||
DatabaseHelper.setMigrationScheduleToDatastorePrimaryNoAsync(clock);
|
||||
DomainBase domain = persistActiveDomain("example.tld");
|
||||
HostResource oldHost = persistActiveSubordinateHost(oldHostName(), domain);
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
// The example xml doesn't do a host rename, so reloading the host should work.
|
||||
assertAboutHosts()
|
||||
.that(reloadResourceByForeignKey())
|
||||
.hasLastSuperordinateChange(oldHost.getLastSuperordinateChange())
|
||||
.and()
|
||||
.hasSuperordinateDomain(domain.createVKey())
|
||||
.and()
|
||||
.hasPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.and()
|
||||
.hasLastTransferTime(null)
|
||||
.and()
|
||||
.hasOnlyOneHistoryEntryWhich()
|
||||
.hasType(HistoryEntry.Type.HOST_UPDATE);
|
||||
assertDnsTasksEnqueued("ns1.example.tld");
|
||||
DatabaseHelper.removeDatabaseMigrationSchedule();
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testRename_duringNoAsyncPhase_fails() throws Exception {
|
||||
createTld("tld");
|
||||
persistActiveSubordinateHost(oldHostName(), persistActiveDomain("example.tld"));
|
||||
DatabaseHelper.setMigrationScheduleToDatastorePrimaryNoAsync(clock);
|
||||
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
DatabaseHelper.removeDatabaseMigrationSchedule();
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testModification_duringReadOnlyPhase_fails() throws Exception {
|
||||
createTld("tld");
|
||||
persistActiveSubordinateHost(oldHostName(), persistActiveDomain("example.tld"));
|
||||
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.model.billing;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
@@ -36,6 +37,7 @@ import com.googlecode.objectify.Key;
|
||||
import google.registry.model.EntityTestCase;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
@@ -49,6 +51,7 @@ import google.registry.testing.TestOfyAndSql;
|
||||
import google.registry.testing.TestOfyOnly;
|
||||
import google.registry.testing.TestSqlOnly;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import java.math.BigDecimal;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -474,4 +477,487 @@ public class BillingEventTest extends EntityTestCase {
|
||||
.setParent(domainHistory)
|
||||
.build();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_defaultRenewalPriceBehavior_assertsIsDefault() {
|
||||
assertThat(recurring.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.DEFAULT);
|
||||
assertThat(recurring.getRenewalPrice()).isEmpty();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_getRenewalPriceBehavior_returnsRightBehavior() {
|
||||
BillingEvent.Recurring recurringEvent =
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.setRecurrenceEndTime(END_OF_TIME)));
|
||||
assertThat(recurringEvent.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.NONPREMIUM);
|
||||
assertThat(recurringEvent.getRenewalPrice()).isEmpty();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_setRenewalPriceBehaviorThenBuild_defaultToSpecified() {
|
||||
BillingEvent.Recurring recurringEvent =
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT)
|
||||
.setRecurrenceEndTime(END_OF_TIME)));
|
||||
assertThat(recurringEvent.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.DEFAULT);
|
||||
assertThat(recurringEvent.getRenewalPrice()).isEmpty();
|
||||
BillingEvent.Recurring loadedEntity = loadByEntity(recurringEvent);
|
||||
assertThat(loadedEntity).isEqualTo(recurringEvent);
|
||||
persistResource(
|
||||
loadedEntity
|
||||
.asBuilder()
|
||||
.setRenewalPrice(Money.of(USD, 100))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.build());
|
||||
assertThat(loadByEntity(recurringEvent).getRenewalPriceBehavior())
|
||||
.isEqualTo(RenewalPriceBehavior.SPECIFIED);
|
||||
assertThat(loadByEntity(recurringEvent).getRenewalPrice()).hasValue(Money.of(USD, 100));
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_setRenewalPriceBehaviorThenBuild_defaultToNonPremium() {
|
||||
BillingEvent.Recurring recurringEvent =
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT)
|
||||
.setRecurrenceEndTime(END_OF_TIME)));
|
||||
assertThat(recurringEvent.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.DEFAULT);
|
||||
assertThat(recurringEvent.getRenewalPrice()).isEmpty();
|
||||
BillingEvent.Recurring loadedEntity = loadByEntity(recurringEvent);
|
||||
assertThat(loadedEntity).isEqualTo(recurringEvent);
|
||||
persistResource(
|
||||
loadedEntity.asBuilder().setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM).build());
|
||||
assertThat(loadByEntity(recurringEvent).getRenewalPriceBehavior())
|
||||
.isEqualTo(RenewalPriceBehavior.NONPREMIUM);
|
||||
assertThat(loadByEntity(recurringEvent).getRenewalPrice()).isEmpty();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_setRenewalPriceBehaviorThenBuild_nonPremiumToSpecified() {
|
||||
BillingEvent.Recurring recurringEvent =
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.setRecurrenceEndTime(END_OF_TIME)));
|
||||
assertThat(recurringEvent.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.NONPREMIUM);
|
||||
assertThat(recurringEvent.getRenewalPrice()).isEmpty();
|
||||
BillingEvent.Recurring loadedEntity = loadByEntity(recurringEvent);
|
||||
assertThat(loadedEntity).isEqualTo(recurringEvent);
|
||||
persistResource(
|
||||
loadedEntity
|
||||
.asBuilder()
|
||||
.setRenewalPrice(Money.of(USD, 100))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.build());
|
||||
assertThat(loadByEntity(recurringEvent).getRenewalPriceBehavior())
|
||||
.isEqualTo(RenewalPriceBehavior.SPECIFIED);
|
||||
assertThat(loadByEntity(recurringEvent).getRenewalPrice()).hasValue(Money.of(USD, 100));
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_setRenewalPriceBehaviorThenBuild_nonPremiumToDefault() {
|
||||
BillingEvent.Recurring recurringEvent =
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.setRecurrenceEndTime(END_OF_TIME)));
|
||||
assertThat(recurringEvent.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.NONPREMIUM);
|
||||
assertThat(recurringEvent.getRenewalPrice()).isEmpty();
|
||||
BillingEvent.Recurring loadedEntity = loadByEntity(recurringEvent);
|
||||
assertThat(loadedEntity).isEqualTo(recurringEvent);
|
||||
persistResource(
|
||||
loadedEntity.asBuilder().setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT).build());
|
||||
assertThat(loadByEntity(recurringEvent).getRenewalPriceBehavior())
|
||||
.isEqualTo(RenewalPriceBehavior.DEFAULT);
|
||||
assertThat(loadByEntity(recurringEvent).getRenewalPrice()).isEmpty();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_setRenewalPriceBehaviorThenBuild_specifiedToDefault() {
|
||||
BillingEvent.Recurring recurringEvent =
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 100))
|
||||
.setRecurrenceEndTime(END_OF_TIME)));
|
||||
assertThat(recurringEvent.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.SPECIFIED);
|
||||
assertThat(recurringEvent.getRenewalPrice()).hasValue(Money.of(USD, 100));
|
||||
BillingEvent.Recurring loadedEntity = loadByEntity(recurringEvent);
|
||||
assertThat(loadedEntity).isEqualTo(recurringEvent);
|
||||
persistResource(
|
||||
loadedEntity
|
||||
.asBuilder()
|
||||
.setRenewalPrice(null)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT)
|
||||
.build());
|
||||
assertThat(loadByEntity(recurringEvent).getRenewalPriceBehavior())
|
||||
.isEqualTo(RenewalPriceBehavior.DEFAULT);
|
||||
assertThat(loadByEntity(recurringEvent).getRenewalPrice()).isEmpty();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_setRenewalPriceBehaviorThenBuild_specifiedToNonPremium() {
|
||||
BillingEvent.Recurring recurringEvent =
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 100))
|
||||
.setRecurrenceEndTime(END_OF_TIME)));
|
||||
assertThat(recurringEvent.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.SPECIFIED);
|
||||
assertThat(recurringEvent.getRenewalPrice()).hasValue(Money.of(USD, 100));
|
||||
BillingEvent.Recurring loadedEntity = loadByEntity(recurringEvent);
|
||||
assertThat(loadedEntity).isEqualTo(recurringEvent);
|
||||
persistResource(
|
||||
loadedEntity
|
||||
.asBuilder()
|
||||
.setRenewalPrice(null)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.build());
|
||||
assertThat(loadByEntity(recurringEvent).getRenewalPriceBehavior())
|
||||
.isEqualTo(RenewalPriceBehavior.NONPREMIUM);
|
||||
assertThat(loadByEntity(recurringEvent).getRenewalPrice()).isEmpty();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_setRenewalPriceBehaviorThenBuild_defaultToSpecified_needRenewalPrice() {
|
||||
BillingEvent.Recurring recurringEvent =
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT)
|
||||
.setRecurrenceEndTime(END_OF_TIME)));
|
||||
assertThat(recurringEvent.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.DEFAULT);
|
||||
assertThat(recurringEvent.getRenewalPrice()).isEmpty();
|
||||
BillingEvent.Recurring loadedEntity = loadByEntity(recurringEvent);
|
||||
assertThat(loadedEntity).isEqualTo(recurringEvent);
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
loadedEntity
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.build());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Renewal price can have a value if and only if the "
|
||||
+ "renewal price behavior is SPECIFIED");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_setRenewalPriceBehaviorThenBuild_defaultToPremium_noNeedToAddRenewalPrice() {
|
||||
BillingEvent.Recurring recurringEvent =
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT)
|
||||
.setRecurrenceEndTime(END_OF_TIME)));
|
||||
assertThat(recurringEvent.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.DEFAULT);
|
||||
assertThat(recurringEvent.getRenewalPrice()).isEmpty();
|
||||
BillingEvent.Recurring loadedEntity = loadByEntity(recurringEvent);
|
||||
assertThat(loadedEntity).isEqualTo(recurringEvent);
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
loadedEntity
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.setRenewalPrice(Money.of(USD, 100))
|
||||
.build());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Renewal price can have a value if and only if the "
|
||||
+ "renewal price behavior is SPECIFIED");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_setRenewalPriceBehaviorThenBuild_nonPremiumToDefault_noNeedToAddRenewalPrice() {
|
||||
BillingEvent.Recurring recurringEvent =
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.setRecurrenceEndTime(END_OF_TIME)));
|
||||
assertThat(recurringEvent.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.NONPREMIUM);
|
||||
assertThat(recurringEvent.getRenewalPrice()).isEmpty();
|
||||
BillingEvent.Recurring loadedEntity = loadByEntity(recurringEvent);
|
||||
assertThat(loadedEntity).isEqualTo(recurringEvent);
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
loadedEntity
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT)
|
||||
.setRenewalPrice(Money.of(USD, 100))
|
||||
.build());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Renewal price can have a value if and only if the "
|
||||
+ "renewal price behavior is SPECIFIED");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_setRenewalPriceBehaviorThenBuild_nonPremiumToSpecified_needRenewalPrice() {
|
||||
BillingEvent.Recurring recurringEvent =
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.setRecurrenceEndTime(END_OF_TIME)));
|
||||
assertThat(recurringEvent.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.NONPREMIUM);
|
||||
assertThat(recurringEvent.getRenewalPrice()).isEmpty();
|
||||
BillingEvent.Recurring loadedEntity = loadByEntity(recurringEvent);
|
||||
assertThat(loadedEntity).isEqualTo(recurringEvent);
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
loadedEntity
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.build());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Renewal price can have a value if and only if the "
|
||||
+ "renewal price behavior is SPECIFIED");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_setRenewalPriceBehaviorThenBuild_specifiedToNonPremium_removeRenewalPrice() {
|
||||
BillingEvent.Recurring recurringEvent =
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 100))
|
||||
.setRecurrenceEndTime(END_OF_TIME)));
|
||||
assertThat(recurringEvent.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.SPECIFIED);
|
||||
assertThat(recurringEvent.getRenewalPrice()).hasValue(Money.of(USD, 100));
|
||||
BillingEvent.Recurring loadedEntity = loadByEntity(recurringEvent);
|
||||
assertThat(loadedEntity).isEqualTo(recurringEvent);
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
loadedEntity
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.build());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Renewal price can have a value if and only if the "
|
||||
+ "renewal price behavior is SPECIFIED");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_setRenewalPriceBehaviorThenBuild_specifiedToDefault_removeRenewalPrice() {
|
||||
BillingEvent.Recurring recurringEvent =
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 100))
|
||||
.setRecurrenceEndTime(END_OF_TIME)));
|
||||
assertThat(recurringEvent.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.SPECIFIED);
|
||||
assertThat(recurringEvent.getRenewalPrice()).hasValue(Money.of(USD, 100));
|
||||
BillingEvent.Recurring loadedEntity = loadByEntity(recurringEvent);
|
||||
assertThat(loadedEntity).isEqualTo(recurringEvent);
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
loadedEntity
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT)
|
||||
.build());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Renewal price can have a value if and only if the "
|
||||
+ "renewal price behavior is SPECIFIED");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_buildWithDefaultRenewalBehavior() {
|
||||
BillingEvent.Recurring recurringEvent =
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, BigDecimal.valueOf(100)))
|
||||
.setRecurrenceEndTime(END_OF_TIME)));
|
||||
assertThat(recurringEvent.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.SPECIFIED);
|
||||
assertThat(recurringEvent.getRenewalPrice()).hasValue(Money.of(USD, 100));
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_buildWithNonPremiumRenewalBehavior() {
|
||||
BillingEvent.Recurring recurringEvent =
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.setRecurrenceEndTime(END_OF_TIME)));
|
||||
assertThat(recurringEvent.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.NONPREMIUM);
|
||||
assertThat(loadByEntity(recurringEvent).getRenewalPrice()).isEmpty();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_buildWithSpecifiedRenewalBehavior() {
|
||||
BillingEvent.Recurring recurringEvent =
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, BigDecimal.valueOf(100)))
|
||||
.setRecurrenceEndTime(END_OF_TIME)));
|
||||
assertThat(recurringEvent.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.SPECIFIED);
|
||||
assertThat(recurringEvent.getRenewalPrice()).hasValue(Money.of(USD, 100));
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_buildWithSpecifiedRenewalBehavior_requiresNonNullRenewalPrice() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.build());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Renewal price can have a value if and only if the "
|
||||
+ "renewal price behavior is SPECIFIED");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_buildWithNonPremiumRenewalBehavior_requiresNullRenewalPrice() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.setRenewalPrice(Money.of(USD, BigDecimal.valueOf(100)))
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.build());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Renewal price can have a value if and only if the "
|
||||
+ "renewal price behavior is SPECIFIED");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_buildWithDefaultRenewalBehavior_requiresNullRenewalPrice() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(domainHistory)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT)
|
||||
.setRenewalPrice(Money.of(USD, BigDecimal.valueOf(100)))
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.build());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Renewal price can have a value if and only if the "
|
||||
+ "renewal price behavior is SPECIFIED");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.model.common;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.DATASTORE_ONLY;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.DATASTORE_PRIMARY;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.DATASTORE_PRIMARY_NO_ASYNC;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.DATASTORE_PRIMARY_READ_ONLY;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.SQL_ONLY;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.SQL_PRIMARY;
|
||||
@@ -71,10 +72,12 @@ public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
|
||||
runValidTransition(DATASTORE_ONLY, DATASTORE_PRIMARY);
|
||||
|
||||
runValidTransition(DATASTORE_PRIMARY, DATASTORE_ONLY);
|
||||
runValidTransition(DATASTORE_PRIMARY, DATASTORE_PRIMARY_READ_ONLY);
|
||||
runValidTransition(DATASTORE_PRIMARY, DATASTORE_PRIMARY_NO_ASYNC);
|
||||
runValidTransition(DATASTORE_PRIMARY_NO_ASYNC, DATASTORE_PRIMARY_READ_ONLY);
|
||||
|
||||
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, DATASTORE_ONLY);
|
||||
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, DATASTORE_PRIMARY);
|
||||
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, DATASTORE_PRIMARY_NO_ASYNC);
|
||||
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, SQL_PRIMARY_READ_ONLY);
|
||||
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, SQL_PRIMARY);
|
||||
|
||||
@@ -94,6 +97,7 @@ public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
|
||||
runInvalidTransition(DATASTORE_ONLY, SQL_PRIMARY);
|
||||
runInvalidTransition(DATASTORE_ONLY, SQL_ONLY);
|
||||
|
||||
runInvalidTransition(DATASTORE_PRIMARY, DATASTORE_PRIMARY_READ_ONLY);
|
||||
runInvalidTransition(DATASTORE_PRIMARY, SQL_PRIMARY_READ_ONLY);
|
||||
runInvalidTransition(DATASTORE_PRIMARY, SQL_PRIMARY);
|
||||
runInvalidTransition(DATASTORE_PRIMARY, SQL_ONLY);
|
||||
@@ -124,7 +128,8 @@ public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
|
||||
ImmutableSortedMap.<DateTime, MigrationState>naturalOrder()
|
||||
.put(START_OF_TIME, DATASTORE_ONLY)
|
||||
.put(startTime.plusHours(1), DATASTORE_PRIMARY)
|
||||
.put(startTime.plusHours(2), DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(startTime.plusHours(2), DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.put(startTime.plusHours(3), DATASTORE_PRIMARY_READ_ONLY)
|
||||
.build();
|
||||
assertThat(
|
||||
assertThrows(
|
||||
@@ -163,7 +168,8 @@ public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
|
||||
fakeClock.setTo(START_OF_TIME.plusDays(1));
|
||||
AllocationToken token =
|
||||
new AllocationToken.Builder().setToken("token").setTokenType(TokenType.SINGLE_USE).build();
|
||||
runValidTransition(DATASTORE_PRIMARY, DATASTORE_PRIMARY_READ_ONLY);
|
||||
runValidTransition(DATASTORE_PRIMARY, DATASTORE_PRIMARY_NO_ASYNC);
|
||||
runValidTransition(DATASTORE_PRIMARY_NO_ASYNC, DATASTORE_PRIMARY_READ_ONLY);
|
||||
assertThrows(ReadOnlyModeException.class, () -> persistResource(token));
|
||||
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, SQL_PRIMARY_READ_ONLY);
|
||||
assertThrows(ReadOnlyModeException.class, () -> persistResource(token));
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.EntityTestCase;
|
||||
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
@@ -160,6 +161,52 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
assertThat(tokenAfterPersisting.getCreationTime()).hasValue(fakeClock.nowUtc());
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testgetRenewalBehavior_returnsDefaultRenewBehavior() {
|
||||
assertThat(
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.build())
|
||||
.getRenewalPriceBehavior())
|
||||
.isEqualTo(RenewalPriceBehavior.DEFAULT);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testsetRenewalBehavior_assertsRenewalBehaviorIsNotDefault() {
|
||||
assertThat(
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.build())
|
||||
.getRenewalPriceBehavior())
|
||||
.isEqualTo(RenewalPriceBehavior.SPECIFIED);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testsetRenewalBehavior_assertRenewalBehaviorIsModified() {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.build());
|
||||
AllocationToken loadedToken = loadByEntity(token);
|
||||
assertThat(token).isEqualTo(loadedToken);
|
||||
AllocationToken modifiedToken =
|
||||
persistResource(
|
||||
loadedToken
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.build());
|
||||
assertThat(loadByEntity(token).getRenewalPriceBehavior())
|
||||
.isEqualTo(RenewalPriceBehavior.SPECIFIED);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSetCreationTime_cantCallMoreThanOnce() {
|
||||
AllocationToken.Builder builder =
|
||||
|
||||
@@ -44,6 +44,7 @@ import google.registry.model.server.Lock;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.JpaTransactionManagerImpl;
|
||||
import google.registry.persistence.transaction.TransactionEntity;
|
||||
import google.registry.persistence.transaction.TransactionManagerFactory;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
@@ -52,6 +53,7 @@ import google.registry.testing.InjectExtension;
|
||||
import google.registry.testing.ReplayExtension;
|
||||
import google.registry.testing.TestObject;
|
||||
import google.registry.util.RequestStatusChecker;
|
||||
import google.registry.util.SystemClock;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
@@ -105,6 +107,7 @@ public class ReplicateToDatastoreActionTest {
|
||||
void tearDown() {
|
||||
DatabaseHelper.removeDatabaseMigrationSchedule();
|
||||
fakeClock.disableAutoIncrement();
|
||||
TransactionManagerFactory.setClockForTesting(new SystemClock());
|
||||
}
|
||||
|
||||
@RetryingTest(4)
|
||||
@@ -299,13 +302,13 @@ public class ReplicateToDatastoreActionTest {
|
||||
// records.
|
||||
action.run();
|
||||
Truth8.assertThat(ofyTm().transact(() -> ofyTm().loadByKeyIfPresent(bar.key()))).isPresent();
|
||||
assertThat(ofyTm().loadAllOf(ReplayGap.class).size()).isEqualTo(30);
|
||||
assertThat(ofyTm().loadAllOf(ReplayGap.class)).hasSize(30);
|
||||
|
||||
// Verify that we can clean up this many gap records after expiration.
|
||||
fakeClock.advanceBy(Duration.millis(ReplicateToDatastoreAction.MAX_GAP_RETENTION_MILLIS + 1));
|
||||
resetAction();
|
||||
action.run();
|
||||
assertThat(ofyTm().loadAllOf(ReplayGap.class).size()).isEqualTo(0);
|
||||
assertThat(ofyTm().loadAllOf(ReplayGap.class)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -366,8 +369,9 @@ public class ReplicateToDatastoreActionTest {
|
||||
ImmutableSortedMap.<DateTime, MigrationState>naturalOrder()
|
||||
.put(START_OF_TIME, MigrationState.DATASTORE_ONLY)
|
||||
.put(START_OF_TIME.plusHours(1), MigrationState.DATASTORE_PRIMARY)
|
||||
.put(START_OF_TIME.plusHours(2), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusHours(3), MigrationState.SQL_PRIMARY)
|
||||
.put(START_OF_TIME.plusHours(2), MigrationState.DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.put(START_OF_TIME.plusHours(3), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusHours(4), MigrationState.SQL_PRIMARY)
|
||||
.put(now.plusHours(1), MigrationState.SQL_PRIMARY_READ_ONLY)
|
||||
.put(now.plusHours(2), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(now.plusHours(3), MigrationState.DATASTORE_PRIMARY)
|
||||
@@ -391,6 +395,50 @@ public class ReplicateToDatastoreActionTest {
|
||||
+ " DATASTORE_PRIMARY.");
|
||||
}
|
||||
|
||||
@Test
|
||||
void replicationWorksInReadOnly() {
|
||||
|
||||
// Put us in SQL primary now, readonly in an hour, then in datastore primary after 25 hours.
|
||||
// And we'll need the TransactionManagerFactory to use the fake clock.
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
TransactionManagerFactory.setClockForTesting(fakeClock);
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
ImmutableSortedMap.<DateTime, MigrationState>naturalOrder()
|
||||
.put(START_OF_TIME, MigrationState.DATASTORE_ONLY)
|
||||
.put(START_OF_TIME.plusHours(1), MigrationState.DATASTORE_PRIMARY)
|
||||
.put(START_OF_TIME.plusHours(2), MigrationState.DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.put(START_OF_TIME.plusHours(3), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusHours(4), MigrationState.SQL_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusHours(5), MigrationState.SQL_PRIMARY)
|
||||
.put(now.plusHours(1), MigrationState.SQL_PRIMARY_READ_ONLY)
|
||||
.put(now.plusHours(25), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(now.plusHours(26), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.build()));
|
||||
|
||||
TestObject foo = TestObject.create("foo");
|
||||
insertInDb(foo);
|
||||
TestObject bar = TestObject.create("bar");
|
||||
insertInDb(bar);
|
||||
TestObject baz = TestObject.create("baz");
|
||||
insertInDb(baz);
|
||||
jpaTm().transact(() -> jpaTm().delete(baz.key()));
|
||||
|
||||
// get to read-only
|
||||
fakeClock.advanceBy(Duration.standardDays(1));
|
||||
|
||||
// process the transaction in readonly.
|
||||
action.run();
|
||||
|
||||
// Forward the next day (datastore primary). Verify that datastore has all of the changes.
|
||||
fakeClock.advanceBy(Duration.standardDays(1));
|
||||
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();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_cannotAcquireLock() {
|
||||
assumeTrue(ReplayExtension.replayTestsEnabled());
|
||||
|
||||
@@ -47,7 +47,9 @@ public class DatabaseMigrationScheduleTransitionConverterTest {
|
||||
MigrationState.DATASTORE_ONLY,
|
||||
DateTime.parse("2001-01-01T00:00:00.0Z"),
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
DateTime.parse("2002-01-01T00:00:00.0Z"),
|
||||
DateTime.parse("2002-01-01T01:00:00.0Z"),
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
|
||||
DateTime.parse("2002-01-01T02:00:00.0Z"),
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
|
||||
DateTime.parse("2002-01-02T00:00:00.0Z"),
|
||||
MigrationState.SQL_PRIMARY,
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.persistence.converter;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.ImmutableObject;
|
||||
@@ -23,6 +24,7 @@ import google.registry.model.replay.EntityTest.EntityForTesting;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -34,9 +36,11 @@ import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.PersistenceException;
|
||||
import org.hibernate.annotations.Columns;
|
||||
import org.hibernate.annotations.Type;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.IllegalCurrencyException;
|
||||
import org.joda.money.Money;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
@@ -65,7 +69,7 @@ public class JodaMoneyConverterTest {
|
||||
.createNativeQuery(
|
||||
"SELECT amount, currency FROM \"TestEntity\" WHERE name = 'id'")
|
||||
.getResultList());
|
||||
assertThat(result.size()).isEqualTo(1);
|
||||
assertThat(result).hasSize(1);
|
||||
// The amount property, when loaded as a raw value, has the same scale as the table column,
|
||||
// which is 2.
|
||||
assertThat(Arrays.asList((Object[]) result.get(0)))
|
||||
@@ -91,7 +95,7 @@ public class JodaMoneyConverterTest {
|
||||
.createNativeQuery(
|
||||
"SELECT amount, currency FROM \"TestEntity\" WHERE name = 'id'")
|
||||
.getResultList());
|
||||
assertThat(result.size()).isEqualTo(1);
|
||||
assertThat(result).hasSize(1);
|
||||
/* The amount property, when loaded as a raw value, has the same scale as the table column,
|
||||
which is 2. */
|
||||
assertThat(Arrays.asList((Object[]) result.get(0)))
|
||||
@@ -136,7 +140,7 @@ public class JodaMoneyConverterTest {
|
||||
"SELECT my_amount, my_currency, your_amount, your_currency FROM"
|
||||
+ " \"ComplexTestEntity\" WHERE name = 'id'")
|
||||
.getResultList());
|
||||
assertThat(result.size()).isEqualTo(1);
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(Arrays.asList((Object[]) result.get(0)))
|
||||
.containsExactly(
|
||||
BigDecimal.valueOf(100).setScale(2), "USD", BigDecimal.valueOf(80).setScale(2), "GBP")
|
||||
@@ -153,7 +157,7 @@ public class JodaMoneyConverterTest {
|
||||
.getResultList());
|
||||
ComplexTestEntity persisted =
|
||||
jpaTm().transact(() -> jpaTm().getEntityManager().find(ComplexTestEntity.class, "id"));
|
||||
assertThat(result.size()).isEqualTo(1);
|
||||
assertThat(result).hasSize(1);
|
||||
|
||||
assertThat(Arrays.asList((Object[]) result.get(0)))
|
||||
.containsExactly(BigDecimal.valueOf(2000).setScale(2), "JPY")
|
||||
@@ -164,6 +168,124 @@ public class JodaMoneyConverterTest {
|
||||
assertThat(persisted.moneyMap).containsExactlyEntriesIn(moneyMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implicit test cases for @override method @nullSafeGet when constructing {@link Money} object
|
||||
* with null/invalid column(s).
|
||||
*/
|
||||
@Test
|
||||
void testNullSafeGet_nullAmountNullCurrency_returnsNull() throws SQLException {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createNativeQuery(
|
||||
"INSERT INTO \"TestEntity\" (name, amount, currency) VALUES('id', null,"
|
||||
+ " null)")
|
||||
.executeUpdate());
|
||||
List<?> result =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createNativeQuery(
|
||||
"SELECT amount, currency FROM \"TestEntity\" WHERE name = 'id'")
|
||||
.getResultList());
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(Arrays.asList((Object[]) result.get(0))).containsExactly(null, null).inOrder();
|
||||
assertThat(
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createQuery("SELECT money FROM TestEntity WHERE name = 'id'")
|
||||
.getResultList())
|
||||
.get(0))
|
||||
.isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullSafeGet_nullAMountValidCurrency_throwsHibernateException() throws SQLException {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createNativeQuery(
|
||||
"INSERT INTO \"TestEntity\" (name, amount, currency) VALUES('id', null,"
|
||||
+ " 'USD')")
|
||||
.executeUpdate());
|
||||
List<?> result =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createNativeQuery(
|
||||
"SELECT amount, currency FROM \"TestEntity\" WHERE name = 'id'")
|
||||
.getResultList());
|
||||
assertThat(Arrays.asList((Object[]) result.get(0))).containsExactly(null, "USD");
|
||||
// CurrencyUnit.of() throws HibernateException for invalid currency which leads to persistance
|
||||
// error
|
||||
PersistenceException thrown =
|
||||
assertThrows(
|
||||
PersistenceException.class,
|
||||
() ->
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createQuery("SELECT money FROM TestEntity WHERE name = 'id'")
|
||||
.getResultList()));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"org.hibernate.HibernateException: Mismatching null state between currency 'USD' and"
|
||||
+ " amount 'null'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullSafeGet_nullAMountInValidCurrency_throwsIllegalCurrencyException()
|
||||
throws SQLException {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createNativeQuery(
|
||||
"INSERT INTO \"TestEntity\" (name, amount, currency) VALUES('id', 100,"
|
||||
+ " 'INVALIDCURRENCY')")
|
||||
.executeUpdate());
|
||||
List<?> result =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createNativeQuery(
|
||||
"SELECT amount, currency FROM \"TestEntity\" WHERE name = 'id'")
|
||||
.getResultList());
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(Arrays.asList((Object[]) result.get(0)))
|
||||
.containsExactly(BigDecimal.valueOf(100).setScale(2), "INVALIDCURRENCY")
|
||||
.inOrder();
|
||||
IllegalCurrencyException thrown =
|
||||
assertThrows(
|
||||
IllegalCurrencyException.class,
|
||||
() ->
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createQuery("SELECT money FROM TestEntity WHERE name = 'id'")
|
||||
.getResultList()));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Unknown currency 'INVALIDCURRENCY'");
|
||||
}
|
||||
|
||||
// Override entity name to exclude outer-class name in table name. Not necessary if class is not
|
||||
// inner class.
|
||||
@Entity(name = "TestEntity")
|
||||
|
||||
@@ -170,7 +170,7 @@ public class BouncyCastleTest {
|
||||
try (ByteArrayInputStream input = new ByteArrayInputStream(signatureFileData)) {
|
||||
PGPObjectFactory pgpFact = new BcPGPObjectFactory(input);
|
||||
PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject();
|
||||
assertThat(sigList.size()).isEqualTo(1);
|
||||
assertThat(sigList).hasSize(1);
|
||||
sig = sigList.get(0);
|
||||
}
|
||||
|
||||
@@ -212,8 +212,8 @@ public class BouncyCastleTest {
|
||||
PGPObjectFactory pgpFact = new BcPGPObjectFactory(input);
|
||||
PGPOnePassSignatureList onePassList = (PGPOnePassSignatureList) pgpFact.nextObject();
|
||||
PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject();
|
||||
assertThat(onePassList.size()).isEqualTo(1);
|
||||
assertThat(sigList.size()).isEqualTo(1);
|
||||
assertThat(onePassList).hasSize(1);
|
||||
assertThat(sigList).hasSize(1);
|
||||
onePass = onePassList.get(0);
|
||||
sig = sigList.get(0);
|
||||
}
|
||||
@@ -258,7 +258,7 @@ public class BouncyCastleTest {
|
||||
try (ByteArrayInputStream input = new ByteArrayInputStream(encryptedData)) {
|
||||
PGPObjectFactory pgpFact = new BcPGPObjectFactory(input);
|
||||
PGPEncryptedDataList encDataList = (PGPEncryptedDataList) pgpFact.nextObject();
|
||||
assertThat(encDataList.size()).isEqualTo(1);
|
||||
assertThat(encDataList).hasSize(1);
|
||||
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) encDataList.get(0);
|
||||
assertThat(encData.getKeyID()).isEqualTo(publicKey.getKeyID());
|
||||
assertThat(encData.getKeyID()).isEqualTo(privateKey.getKeyID());
|
||||
@@ -302,7 +302,7 @@ public class BouncyCastleTest {
|
||||
try (ByteArrayInputStream input = new ByteArrayInputStream(encryptedData)) {
|
||||
PGPObjectFactory pgpFact = new BcPGPObjectFactory(input);
|
||||
PGPEncryptedDataList encDataList = (PGPEncryptedDataList) pgpFact.nextObject();
|
||||
assertThat(encDataList.size()).isEqualTo(1);
|
||||
assertThat(encDataList).hasSize(1);
|
||||
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) encDataList.get(0);
|
||||
// Bob loads the private key to which the message is addressed.
|
||||
PGPPrivateKey privateKey =
|
||||
@@ -350,7 +350,7 @@ public class BouncyCastleTest {
|
||||
try (ByteArrayInputStream input = new ByteArrayInputStream(encryptedData)) {
|
||||
PGPObjectFactory pgpFact = new BcPGPObjectFactory(input);
|
||||
PGPEncryptedDataList encDataList = (PGPEncryptedDataList) pgpFact.nextObject();
|
||||
assertThat(encDataList.size()).isEqualTo(1);
|
||||
assertThat(encDataList).hasSize(1);
|
||||
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) encDataList.get(0);
|
||||
// Bob loads the private key to which the message is addressed.
|
||||
PGPPrivateKey privateKey =
|
||||
|
||||
@@ -53,7 +53,7 @@ class JsonResponseTest {
|
||||
void testSetHeader() {
|
||||
jsonResponse.setHeader("header", "value");
|
||||
Map<String, Object> headerMap = fakeResponse.getHeaders();
|
||||
assertThat(headerMap.size()).isEqualTo(1);
|
||||
assertThat(headerMap).hasSize(1);
|
||||
assertThat(headerMap.get("header")).isEqualTo("value");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ public class CloudTasksHelper implements Serializable {
|
||||
*/
|
||||
public void assertTasksEnqueued(String queueName, Collection<TaskMatcher> taskMatchers) {
|
||||
List<Task> tasks = getTestTasksFor(queueName);
|
||||
assertThat(tasks.size()).isEqualTo(taskMatchers.size());
|
||||
assertThat(tasks).hasSize(taskMatchers.size());
|
||||
for (final TaskMatcher taskMatcher : taskMatchers) {
|
||||
try {
|
||||
tasks.remove(tasks.stream().filter(taskMatcher).findFirst().get());
|
||||
|
||||
@@ -1429,6 +1429,33 @@ public class DatabaseHelper {
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a DATASTORE_PRIMARY_NO_ASYNC state on the {@link DatabaseMigrationStateSchedule}.
|
||||
*
|
||||
* <p>In order to allow for tests to manipulate the clock how they need, we start the transitions
|
||||
* one millisecond after the clock's current time (in case the clock's current value is
|
||||
* START_OF_TIME). We then advance the clock one second so that we're in the
|
||||
* DATASTORE_PRIMARY_READ_ONLY phase.
|
||||
*
|
||||
* <p>We must use the current time, otherwise the setting of the migration state will fail due to
|
||||
* an invalid transition.
|
||||
*/
|
||||
public static void setMigrationScheduleToDatastorePrimaryNoAsync(FakeClock fakeClock) {
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
ImmutableSortedMap.of(
|
||||
START_OF_TIME,
|
||||
MigrationState.DATASTORE_ONLY,
|
||||
now.plusMillis(1),
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
now.plusMillis(2),
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC)));
|
||||
fakeClock.advanceBy(Duration.standardSeconds(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a DATASTORE_PRIMARY_READ_ONLY state on the {@link DatabaseMigrationStateSchedule}.
|
||||
*
|
||||
@@ -1452,6 +1479,8 @@ public class DatabaseHelper {
|
||||
now.plusMillis(1),
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
now.plusMillis(2),
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
|
||||
now.plusMillis(3),
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY)));
|
||||
fakeClock.advanceBy(Duration.standardSeconds(1));
|
||||
}
|
||||
@@ -1478,8 +1507,10 @@ public class DatabaseHelper {
|
||||
now.plusMillis(1),
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
now.plusMillis(2),
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
|
||||
now.plusMillis(3),
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
|
||||
now.plusMillis(4),
|
||||
MigrationState.SQL_PRIMARY)));
|
||||
fakeClock.advanceBy(Duration.standardSeconds(1));
|
||||
}
|
||||
|
||||
@@ -37,6 +37,12 @@ class CheckDomainClaimsCommandTest extends EppToolCommandTestCase<CheckDomainCla
|
||||
eppVerifier.expectDryRun().verifySent("domain_check_claims.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_canonicalizesInput() throws Exception {
|
||||
runCommand("--client=NewRegistrar", "exaMPle.TLD");
|
||||
eppVerifier.expectDryRun().verifySent("domain_check_claims.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_multipleTlds() throws Exception {
|
||||
runCommand("--client=NewRegistrar", "example.tld", "example.tld2");
|
||||
|
||||
@@ -29,7 +29,7 @@ class CreateHostCommandTest extends EppToolCommandTestCase<CreateHostCommand> {
|
||||
createTld("tld");
|
||||
runCommandForced(
|
||||
"--client=NewRegistrar",
|
||||
"--host=example.tld",
|
||||
"--host=example.TLD",
|
||||
"--addresses=162.100.102.99,2001:0db8:85a3:0000:0000:8a2e:0370:7334,4.5.6.7");
|
||||
eppVerifier.verifySent("host_create_complete.xml");
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.createTlds;
|
||||
@@ -546,7 +547,7 @@ public final class DomainLockUtilsTest {
|
||||
assertThat(loadByEntity(domain).getStatusValues()).containsNoneIn(REGISTRY_LOCK_STATUSES);
|
||||
ImmutableList<DomainHistory> historyEntries =
|
||||
getHistoryEntriesOfType(domain, HistoryEntry.Type.DOMAIN_UPDATE, DomainHistory.class);
|
||||
assertThat(historyEntries.size()).isEqualTo(2);
|
||||
assertThat(historyEntries).hasSize(2);
|
||||
historyEntries.forEach(
|
||||
entry -> {
|
||||
assertThat(entry.getRequestedByRegistrar()).isEqualTo(!isAdmin);
|
||||
|
||||
@@ -55,7 +55,8 @@ class EnqueuePollMessageCommandTest extends CommandTestCase<EnqueuePollMessageCo
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_domainAndMessage() throws Exception {
|
||||
runCommandForced("--domain=example.tld", "--message=This domain is bad");
|
||||
// Test canonicalization to lowercase example.tld as well.
|
||||
runCommandForced("--domain=EXAMPLE.TLD", "--message=This domain is bad");
|
||||
|
||||
HistoryEntry synthetic = getOnlyHistoryEntryOfType(domain, SYNTHETIC);
|
||||
assertAboutHistoryEntries()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user