mirror of
https://github.com/google/nomulus
synced 2026-05-20 06:41:51 +00:00
Compare commits
21 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0af81ecdf | ||
|
|
f273783894 | ||
|
|
0dfabe1c64 | ||
|
|
60a011c593 | ||
|
|
7716eebfff | ||
|
|
1e76eeed37 | ||
|
|
147d133aef | ||
|
|
c2e1f2e640 | ||
|
|
5d2639834a | ||
|
|
7912576e3d | ||
|
|
8424c85258 | ||
|
|
e72dd73ed8 | ||
|
|
508d221b94 | ||
|
|
073d0a416a | ||
|
|
f2ead5a0e3 | ||
|
|
212dbbe520 | ||
|
|
8594a61fd4 | ||
|
|
36837eb3e6 | ||
|
|
3a9a8c6557 | ||
|
|
65c2570b8f | ||
|
|
86acaa1b31 |
@@ -36,3 +36,4 @@ Shicong Huang <shicong@google.com>
|
||||
Gustav Brodman <gbrodman@google.com>
|
||||
Sarah Botwinick <sarahbot@google.com>
|
||||
Legina Chen <legina@google.com>
|
||||
Rachel Guan <rachelguan@google.com>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -51,6 +51,7 @@ artifacts {
|
||||
dependencies {
|
||||
def deps = rootProject.dependencyMap
|
||||
|
||||
compile deps['com.github.ben-manes.caffeine:caffeine']
|
||||
compile deps['com.google.code.findbugs:jsr305']
|
||||
compile deps['com.google.guava:guava']
|
||||
compile deps['javax.inject:javax.inject']
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
org.checkerframework:checker-qual:3.8.0
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
org.checkerframework:checker-qual:3.8.0
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
org.checkerframework:checker-qual:3.8.0
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
org.checkerframework:checker-qual:3.8.0
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
org.checkerframework:checker-qual:3.8.0
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
org.checkerframework:checker-qual:3.8.0
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.auto.value:auto-value-annotations:1.7.4
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.flogger:flogger:0.7.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
@@ -16,7 +17,7 @@ joda-time:joda-time:2.9.2
|
||||
junit:junit:4.13.1
|
||||
org.apiguardian:apiguardian-api:1.1.0
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:3.9.1
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.2
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.2
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.auto.value:auto-value-annotations:1.7.4
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.flogger:flogger:0.7.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
@@ -16,7 +17,7 @@ joda-time:joda-time:2.9.2
|
||||
junit:junit:4.13.1
|
||||
org.apiguardian:apiguardian-api:1.1.0
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:3.9.1
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.2
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.2
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.auto.value:auto-value-annotations:1.7.4
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
com.google.flogger:flogger:0.7.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
@@ -17,7 +18,7 @@ joda-time:joda-time:2.9.2
|
||||
junit:junit:4.13.1
|
||||
org.apiguardian:apiguardian-api:1.1.0
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:3.9.1
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.2
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.2
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.auto.value:auto-value-annotations:1.7.4
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
com.google.flogger:flogger:0.7.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
@@ -17,7 +18,7 @@ joda-time:joda-time:2.9.2
|
||||
junit:junit:4.13.1
|
||||
org.apiguardian:apiguardian-api:1.1.0
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:3.9.1
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.2
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.2
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.auto.value:auto-value-annotations:1.7.4
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.flogger:flogger:0.7.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
@@ -15,6 +16,6 @@ javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
junit:junit:4.13.1
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:3.9.1
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.ow2.asm:asm:9.0
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.auto.value:auto-value-annotations:1.7.4
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.flogger:flogger:0.7.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
@@ -15,6 +16,6 @@ javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
junit:junit:4.13.1
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:3.9.1
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.ow2.asm:asm:9.0
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.auto.value:auto-value-annotations:1.7.4
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
com.google.flogger:flogger:0.7.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
@@ -16,6 +17,6 @@ javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
junit:junit:4.13.1
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:3.9.1
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.ow2.asm:asm:9.0
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.auto.value:auto-value-annotations:1.7.4
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
com.google.flogger:flogger:0.7.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
@@ -16,6 +17,6 @@ javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
junit:junit:4.13.1
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:3.9.1
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.ow2.asm:asm:9.0
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
|
||||
package google.registry.testing;
|
||||
|
||||
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.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@@ -29,7 +29,7 @@ public final class SystemInfo {
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final LoadingCache<String, Boolean> hasCommandCache =
|
||||
CacheBuilder.newBuilder()
|
||||
Caffeine.newBuilder()
|
||||
.build(
|
||||
new CacheLoader<String, Boolean>() {
|
||||
@Override
|
||||
|
||||
@@ -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:2.9.3
|
||||
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:2.9.3
|
||||
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:2.9.3
|
||||
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:2.9.3
|
||||
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:2.9.3
|
||||
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:2.9.3
|
||||
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:2.9.3
|
||||
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:2.9.3
|
||||
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:2.9.3
|
||||
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:2.9.3
|
||||
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:2.9.3
|
||||
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:2.9.3
|
||||
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:2.9.3
|
||||
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:2.9.3
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
|
||||
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;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.beam.common.RegistryJpaIO;
|
||||
import google.registry.beam.common.RegistryJpaIO.Read;
|
||||
import google.registry.beam.invoicing.BillingEvent.InvoiceGroupingKey;
|
||||
@@ -36,6 +36,7 @@ import java.time.LocalTime;
|
||||
import java.time.YearMonth;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.beam.sdk.Pipeline;
|
||||
@@ -75,6 +76,8 @@ public class InvoicingPipeline implements Serializable {
|
||||
private static final Pattern SQL_COMMENT_REGEX =
|
||||
Pattern.compile("^\\s*--.*\\n", Pattern.MULTILINE);
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private final InvoicingPipelineOptions options;
|
||||
|
||||
InvoicingPipeline(InvoicingPipelineOptions options) {
|
||||
@@ -115,38 +118,44 @@ public class InvoicingPipeline implements Serializable {
|
||||
InvoicingPipelineOptions options, Pipeline pipeline) {
|
||||
Read<Object[], BillingEvent> read =
|
||||
RegistryJpaIO.read(
|
||||
makeCloudSqlQuery(options.getYearMonth()), false, InvoicingPipeline::parseRow);
|
||||
makeCloudSqlQuery(options.getYearMonth()), false, row -> parseRow(row).orElse(null));
|
||||
|
||||
return pipeline.apply("Read BillingEvents from Cloud SQL", read);
|
||||
PCollection<BillingEvent> billingEventsWithNulls =
|
||||
pipeline.apply("Read BillingEvents from Cloud SQL", read);
|
||||
|
||||
// Remove null billing events
|
||||
return billingEventsWithNulls.apply(Filter.by(Objects::nonNull));
|
||||
}
|
||||
|
||||
private static BillingEvent parseRow(Object[] row) {
|
||||
private static Optional<BillingEvent> parseRow(Object[] row) {
|
||||
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);
|
||||
if (!registrar.getBillingAccountMap().containsKey(currency)) {
|
||||
logger.atSevere().log(
|
||||
"Registrar %s does not have a product account key for the currency unit: %s",
|
||||
registrar.getRegistrarId(), currency);
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return BillingEvent.create(
|
||||
oneTime.getId(),
|
||||
DateTimeUtils.toZonedDateTime(oneTime.getBillingTime(), ZoneId.of("UTC")),
|
||||
DateTimeUtils.toZonedDateTime(oneTime.getEventTime(), ZoneId.of("UTC")),
|
||||
registrar.getRegistrarId(),
|
||||
registrar.getBillingAccountMap().get(currency),
|
||||
registrar.getPoNumber().orElse(""),
|
||||
DomainNameUtils.getTldFromDomainName(oneTime.getTargetId()),
|
||||
oneTime.getReason().toString(),
|
||||
oneTime.getTargetId(),
|
||||
oneTime.getDomainRepoId(),
|
||||
Optional.ofNullable(oneTime.getPeriodYears()).orElse(0),
|
||||
oneTime.getCost().getCurrencyUnit().toString(),
|
||||
oneTime.getCost().getAmount().doubleValue(),
|
||||
String.join(
|
||||
" ", oneTime.getFlags().stream().map(Flag::toString).collect(toImmutableSet())));
|
||||
return Optional.of(
|
||||
BillingEvent.create(
|
||||
oneTime.getId(),
|
||||
DateTimeUtils.toZonedDateTime(oneTime.getBillingTime(), ZoneId.of("UTC")),
|
||||
DateTimeUtils.toZonedDateTime(oneTime.getEventTime(), ZoneId.of("UTC")),
|
||||
registrar.getRegistrarId(),
|
||||
registrar.getBillingAccountMap().get(currency),
|
||||
registrar.getPoNumber().orElse(""),
|
||||
DomainNameUtils.getTldFromDomainName(oneTime.getTargetId()),
|
||||
oneTime.getReason().toString(),
|
||||
oneTime.getTargetId(),
|
||||
oneTime.getDomainRepoId(),
|
||||
Optional.ofNullable(oneTime.getPeriodYears()).orElse(0),
|
||||
oneTime.getCost().getCurrencyUnit().toString(),
|
||||
oneTime.getCost().getAmount().doubleValue(),
|
||||
String.join(
|
||||
" ", oneTime.getFlags().stream().map(Flag::toString).collect(toImmutableSet()))));
|
||||
}
|
||||
|
||||
/** Transform that converts a {@code BillingEvent} into an invoice CSV row. */
|
||||
|
||||
@@ -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>
|
||||
@@ -36,19 +36,6 @@
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url>/_dr/task/rdeStaging?beam=true</url>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML
|
||||
document using the Beam pipeline regardless of the current TM
|
||||
configuration and streams it to cloud storage. It does not trigger the
|
||||
subsequent upload tasks and is meant to run parallel with the main cron
|
||||
job in order to compare the results from both runs.
|
||||
</description>
|
||||
<schedule>every 8 hours from 00:07 to 20:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-upload&endpoint=/_dr/task/rdeUpload&forEachRealTld]]></url>
|
||||
<description>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,9 @@ import static com.google.common.collect.Maps.transformValues;
|
||||
import static java.util.stream.Collectors.toCollection;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
@@ -59,36 +57,33 @@ public class ModelUtils {
|
||||
|
||||
/** Caches all instance fields on an object, including non-public and inherited fields. */
|
||||
private static final LoadingCache<Class<?>, ImmutableMap<String, Field>> ALL_FIELDS_CACHE =
|
||||
CacheBuilder.newBuilder()
|
||||
CacheUtils.newCacheBuilder()
|
||||
.build(
|
||||
new CacheLoader<Class<?>, ImmutableMap<String, Field>>() {
|
||||
@Override
|
||||
public ImmutableMap<String, Field> load(Class<?> clazz) {
|
||||
Deque<Class<?>> hierarchy = new ArrayDeque<>();
|
||||
// Walk the hierarchy up to but not including ImmutableObject (to ignore
|
||||
// hashCode).
|
||||
for (; clazz != ImmutableObject.class; clazz = clazz.getSuperclass()) {
|
||||
// Add to the front, so that shadowed fields show up later in the list.
|
||||
// This will mean that getFieldValues will show the most derived value.
|
||||
hierarchy.addFirst(clazz);
|
||||
}
|
||||
Map<String, Field> fields = new LinkedHashMap<>();
|
||||
for (Class<?> hierarchyClass : hierarchy) {
|
||||
// Don't use hierarchyClass.getFields() because it only picks up public fields.
|
||||
for (Field field : hierarchyClass.getDeclaredFields()) {
|
||||
if (!Modifier.isStatic(field.getModifiers())) {
|
||||
field.setAccessible(true);
|
||||
fields.put(field.getName(), field);
|
||||
}
|
||||
clazz -> {
|
||||
Deque<Class<?>> hierarchy = new ArrayDeque<>();
|
||||
// Walk the hierarchy up to but not including ImmutableObject (to ignore
|
||||
// hashCode).
|
||||
for (; clazz != ImmutableObject.class; clazz = clazz.getSuperclass()) {
|
||||
// Add to the front, so that shadowed fields show up later in the list.
|
||||
// This will mean that getFieldValues will show the most derived value.
|
||||
hierarchy.addFirst(clazz);
|
||||
}
|
||||
Map<String, Field> fields = new LinkedHashMap<>();
|
||||
for (Class<?> hierarchyClass : hierarchy) {
|
||||
// Don't use hierarchyClass.getFields() because it only picks up public fields.
|
||||
for (Field field : hierarchyClass.getDeclaredFields()) {
|
||||
if (!Modifier.isStatic(field.getModifiers())) {
|
||||
field.setAccessible(true);
|
||||
fields.put(field.getName(), field);
|
||||
}
|
||||
}
|
||||
return ImmutableMap.copyOf(fields);
|
||||
}
|
||||
return ImmutableMap.copyOf(fields);
|
||||
});
|
||||
|
||||
/** Lists all instance fields on an object, including non-public and inherited fields. */
|
||||
public static Map<String, Field> getAllFields(Class<?> clazz) {
|
||||
return ALL_FIELDS_CACHE.getUnchecked(clazz);
|
||||
return ALL_FIELDS_CACHE.get(clazz);
|
||||
}
|
||||
|
||||
/** Return a string representing the persisted schema of a type or enum. */
|
||||
|
||||
@@ -38,6 +38,7 @@ import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.registrar.RegistrarContact;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldState;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.label.PremiumList;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import google.registry.persistence.VKey;
|
||||
@@ -111,6 +112,17 @@ public final class OteAccountBuilder {
|
||||
DateTime.parse("2030-03-01T00:00:00Z"),
|
||||
Money.of(CurrencyUnit.USD, 0));
|
||||
|
||||
/**
|
||||
* The default billing account map applied to all OT&E registrars.
|
||||
*
|
||||
* <p>This contains dummy values for USD and JPY so that OT&E registrars can be granted access
|
||||
* to all existing TLDs in sandbox. Note that OT&E is only on sandbox and thus these dummy
|
||||
* values will never be used in production (the only environment where real invoicing takes
|
||||
* place).
|
||||
*/
|
||||
public static final ImmutableMap<CurrencyUnit, String> DEFAULT_BILLING_ACCOUNT_MAP =
|
||||
ImmutableMap.of(CurrencyUnit.USD, "123", CurrencyUnit.JPY, "456");
|
||||
|
||||
private final ImmutableMap<String, String> registrarIdToTld;
|
||||
private final Registry sunriseTld;
|
||||
private final Registry gaTld;
|
||||
@@ -305,6 +317,7 @@ public final class OteAccountBuilder {
|
||||
.setTldStateTransitions(ImmutableSortedMap.of(START_OF_TIME, initialTldState))
|
||||
.setDnsWriters(ImmutableSet.of("VoidDnsWriter"))
|
||||
.setPremiumList(premiumList.get())
|
||||
.setTldType(TldType.TEST)
|
||||
.setRoidSuffix(
|
||||
String.format(
|
||||
"%S%X",
|
||||
@@ -334,6 +347,7 @@ public final class OteAccountBuilder {
|
||||
.setPhoneNumber("+1.2125550100")
|
||||
.setIcannReferralEmail("nightmare@registrar.test")
|
||||
.setState(Registrar.State.ACTIVE)
|
||||
.setBillingAccountMap(DEFAULT_BILLING_ACCOUNT_MAP)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -18,14 +18,13 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.CacheUtils;
|
||||
import google.registry.model.annotations.DeleteAfterMigration;
|
||||
import google.registry.model.common.TimedTransitionProperty.TimedTransition;
|
||||
import google.registry.model.replay.SqlOnlyEntity;
|
||||
@@ -135,18 +134,8 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition>>
|
||||
// Each instance should cache the migration schedule for five minutes before reloading
|
||||
CACHE =
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(Duration.ofMinutes(5))
|
||||
.build(
|
||||
new CacheLoader<
|
||||
Class<DatabaseMigrationStateSchedule>,
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition>>() {
|
||||
@Override
|
||||
public TimedTransitionProperty<MigrationState, MigrationStateTransition> load(
|
||||
Class<DatabaseMigrationStateSchedule> unused) {
|
||||
return DatabaseMigrationStateSchedule.getUncached();
|
||||
}
|
||||
});
|
||||
CacheUtils.newCacheBuilder(Duration.ofMinutes(5))
|
||||
.build(singletonClazz -> DatabaseMigrationStateSchedule.getUncached());
|
||||
|
||||
// Restrictions on the state transitions, e.g. no going from DATASTORE_ONLY to SQL_ONLY
|
||||
private static final ImmutableMultimap<MigrationState, MigrationState> VALID_STATE_TRANSITIONS =
|
||||
@@ -235,7 +224,7 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements
|
||||
|
||||
/** Loads the currently-set migration schedule from the cache, or the default if none exists. */
|
||||
public static TimedTransitionProperty<MigrationState, MigrationStateTransition> get() {
|
||||
return CACHE.getUnchecked(DatabaseMigrationStateSchedule.class);
|
||||
return CACHE.get(DatabaseMigrationStateSchedule.class);
|
||||
}
|
||||
|
||||
/** Returns the database migration status at the given time. */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ import google.registry.model.common.EntityGroupRoot;
|
||||
import google.registry.model.registrar.Registrar.BillingAccountEntry.CurrencyMapper;
|
||||
import google.registry.model.replay.DatastoreAndSqlEntity;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import java.security.cert.CertificateParsingException;
|
||||
@@ -798,13 +799,9 @@ public class Registrar extends ImmutableObject
|
||||
}
|
||||
|
||||
public Builder setBillingAccountMap(@Nullable Map<CurrencyUnit, String> billingAccountMap) {
|
||||
if (billingAccountMap == null) {
|
||||
getInstance().billingAccountMap = null;
|
||||
} else {
|
||||
getInstance().billingAccountMap =
|
||||
billingAccountMap.entrySet().stream()
|
||||
.collect(toImmutableMap(Map.Entry::getKey, BillingAccountEntry::new));
|
||||
}
|
||||
getInstance().billingAccountMap =
|
||||
nullToEmptyImmutableCopy(billingAccountMap).entrySet().stream()
|
||||
.collect(toImmutableMap(Map.Entry::getKey, BillingAccountEntry::new));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1015,6 +1012,20 @@ public class Registrar extends ImmutableObject
|
||||
String.format(
|
||||
"Supplied IANA ID is not valid for %s registrar type: %s",
|
||||
getInstance().type, getInstance().ianaIdentifier));
|
||||
|
||||
// In order to grant access to real TLDs, the registrar must have a corresponding billing
|
||||
// account ID for that TLD's billing currency.
|
||||
ImmutableSet<String> nonBillableTlds =
|
||||
Registry.get(getInstance().getAllowedTlds()).stream()
|
||||
.filter(r -> r.getTldType() == TldType.REAL)
|
||||
.filter(r -> !getInstance().getBillingAccountMap().containsKey(r.getCurrency()))
|
||||
.map(Registry::getTldStr)
|
||||
.collect(toImmutableSet());
|
||||
checkArgument(
|
||||
nonBillableTlds.isEmpty(),
|
||||
"Cannot set these allowed, real TLDs because their currency is missing "
|
||||
+ "from the billing account map: %s",
|
||||
nonBillableTlds);
|
||||
return cloneEmptyToNull(super.build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,14 @@ package google.registry.model.server;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Ignore;
|
||||
import com.googlecode.objectify.annotation.OnLoad;
|
||||
import com.googlecode.objectify.annotation.Unindex;
|
||||
import google.registry.model.CacheUtils;
|
||||
import google.registry.model.annotations.NotBackedUp;
|
||||
import google.registry.model.annotations.NotBackedUp.Reason;
|
||||
import google.registry.model.common.CrossTldSingleton;
|
||||
@@ -32,7 +31,6 @@ import google.registry.model.replay.NonReplicatedEntity;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.PostLoad;
|
||||
import javax.persistence.Transient;
|
||||
@@ -52,14 +50,7 @@ public class ServerSecret extends CrossTldSingleton implements NonReplicatedEnti
|
||||
* Supplier that can be reset for testing purposes.
|
||||
*/
|
||||
private static final LoadingCache<Class<ServerSecret>, ServerSecret> CACHE =
|
||||
CacheBuilder.newBuilder()
|
||||
.build(
|
||||
new CacheLoader<Class<ServerSecret>, ServerSecret>() {
|
||||
@Override
|
||||
public ServerSecret load(Class<ServerSecret> unused) {
|
||||
return retrieveAndSaveSecret();
|
||||
}
|
||||
});
|
||||
CacheUtils.newCacheBuilder().build(singletonClazz -> retrieveAndSaveSecret());
|
||||
|
||||
private static ServerSecret retrieveAndSaveSecret() {
|
||||
if (tm().isOfy()) {
|
||||
@@ -84,11 +75,7 @@ public class ServerSecret extends CrossTldSingleton implements NonReplicatedEnti
|
||||
|
||||
/** Returns the global ServerSecret instance, creating it if one isn't already in Datastore. */
|
||||
public static ServerSecret get() {
|
||||
try {
|
||||
return CACHE.get(ServerSecret.class);
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return CACHE.get(ServerSecret.class);
|
||||
}
|
||||
|
||||
/** Most significant 8 bytes of the UUID value (stored separately for legacy purposes). */
|
||||
|
||||
@@ -72,7 +72,7 @@ public final class Registries {
|
||||
.stream()
|
||||
.map(Key::getName)
|
||||
.collect(toImmutableSet());
|
||||
return Registry.getAll(tlds).stream()
|
||||
return Registry.get(tlds).stream()
|
||||
.map(e -> Maps.immutableEntry(e.getTldStr(), e.getTldType()))
|
||||
.collect(entriesToImmutableMap());
|
||||
} else {
|
||||
@@ -105,7 +105,7 @@ public final class Registries {
|
||||
|
||||
/** Returns the Registry entities themselves of the given type loaded fresh from Datastore. */
|
||||
public static ImmutableSet<Registry> getTldEntitiesOfType(TldType type) {
|
||||
return Registry.getAll(filterValues(cache.get(), equalTo(type)).keySet());
|
||||
return Registry.get(filterValues(cache.get(), equalTo(type)).keySet());
|
||||
}
|
||||
|
||||
/** Pass-through check that the specified TLD exists, otherwise throw an IAE. */
|
||||
|
||||
@@ -142,10 +142,16 @@ public class Registry extends ImmutableObject
|
||||
|
||||
/** The type of TLD, which determines things like backups and escrow policy. */
|
||||
public enum TldType {
|
||||
/** A real, official TLD. */
|
||||
/**
|
||||
* A real, official TLD (but not necessarily only on production).
|
||||
*
|
||||
* <p>Note that, to avoid unnecessary costly DB writes, {@link
|
||||
* google.registry.model.reporting.DomainTransactionRecord}s are only written out for REAL TLDs
|
||||
* (these transaction records are only used for ICANN reporting purposes).
|
||||
*/
|
||||
REAL,
|
||||
|
||||
/** A test TLD, for the prober. */
|
||||
/** A test TLD, for the prober, OT&E, and other testing purposes. */
|
||||
TEST
|
||||
}
|
||||
|
||||
@@ -232,7 +238,7 @@ public class Registry extends ImmutableObject
|
||||
}
|
||||
|
||||
/** Returns the registry entities for the given TLD strings, throwing if any don't exist. */
|
||||
static ImmutableSet<Registry> getAll(Set<String> tlds) {
|
||||
public static ImmutableSet<Registry> get(Set<String> tlds) {
|
||||
try {
|
||||
ImmutableMap<String, Optional<Registry>> registries = CACHE.getAll(tlds);
|
||||
ImmutableSet<String> missingRegistries =
|
||||
@@ -264,8 +270,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
|
||||
@@ -1001,7 +1006,7 @@ public class Registry extends ImmutableObject
|
||||
instance.renewBillingCostTransitions.checkValidity();
|
||||
instance.eapFeeSchedule.checkValidity();
|
||||
// All costs must be in the expected currency.
|
||||
// TODO(b/21854155): When we move PremiumList into Datastore, verify its currency too.
|
||||
checkArgumentNotNull(instance.getCurrency(), "Currency must be set");
|
||||
checkArgument(
|
||||
instance.getStandardCreateCost().getCurrencyUnit().equals(instance.currency),
|
||||
"Create cost must be in the registry's currency");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -33,8 +33,10 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.ObjectStreamClass;
|
||||
|
||||
/**
|
||||
* A SQL transaction that can be serialized and stored in its own table.
|
||||
@@ -105,7 +107,8 @@ public class Transaction extends ImmutableObject implements Buildable {
|
||||
}
|
||||
|
||||
public static Transaction deserialize(byte[] serializedTransaction) throws IOException {
|
||||
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(serializedTransaction));
|
||||
ObjectInputStream in =
|
||||
new LenientObjectInputStream(new ByteArrayInputStream(serializedTransaction));
|
||||
|
||||
// Verify that the data is what we expect.
|
||||
int version = in.readInt();
|
||||
@@ -304,4 +307,35 @@ public class Transaction extends ImmutableObject implements Buildable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ObjectInputStream that ignores the UIDs of serialized objects.
|
||||
*
|
||||
* <p>We only really need to deserialize VKeys. However, VKeys have a class object associated with
|
||||
* them, and if the class is changed and we haven't defined a serialVersionUID for it, we get an
|
||||
* exception during deserialization.
|
||||
*
|
||||
* <p>It's safe for us to ignore this condition: we only care about attaching the correct local
|
||||
* class object to the VKey. So this class effectively does so by replacing the class descriptor
|
||||
* if it's version UID doesn't match that of the local class.
|
||||
*/
|
||||
private static class LenientObjectInputStream extends ObjectInputStream {
|
||||
|
||||
public LenientObjectInputStream(InputStream in) throws IOException {
|
||||
super(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
|
||||
ObjectStreamClass persistedDescriptor = super.readClassDescriptor();
|
||||
Class localClass = Class.forName(persistedDescriptor.getName());
|
||||
ObjectStreamClass localDescriptor = ObjectStreamClass.lookup(localClass);
|
||||
if (localDescriptor != null) {
|
||||
if (persistedDescriptor.getSerialVersionUID() != localDescriptor.getSerialVersionUID()) {
|
||||
return localDescriptor;
|
||||
}
|
||||
}
|
||||
return persistedDescriptor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -24,14 +24,11 @@ import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import google.registry.flows.certs.CertificateChecker;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.tools.params.KeyValueMapParameter.CurrencyUnitToStringMap;
|
||||
import google.registry.tools.params.OptionalLongParameter;
|
||||
import google.registry.tools.params.OptionalPhoneNumberParameter;
|
||||
@@ -46,7 +43,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
@@ -433,22 +429,6 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
|
||||
// Require a phone passcode.
|
||||
checkArgument(
|
||||
newRegistrar.getPhonePasscode() != null, "--passcode is required for REAL registrars.");
|
||||
// Check if registrar has billing account IDs for the currency of the TLDs that it is
|
||||
// allowed to register.
|
||||
ImmutableSet<CurrencyUnit> tldCurrencies =
|
||||
newRegistrar
|
||||
.getAllowedTlds()
|
||||
.stream()
|
||||
.map(tld -> Registry.get(tld).getCurrency())
|
||||
.collect(toImmutableSet());
|
||||
Set<CurrencyUnit> currenciesWithoutBillingAccountId =
|
||||
newRegistrar.getBillingAccountMap() == null
|
||||
? tldCurrencies
|
||||
: Sets.difference(tldCurrencies, newRegistrar.getBillingAccountMap().keySet());
|
||||
checkArgument(
|
||||
currenciesWithoutBillingAccountId.isEmpty(),
|
||||
"Need billing account map entries for currencies: %s",
|
||||
Joiner.on(' ').join(currenciesWithoutBillingAccountId));
|
||||
}
|
||||
|
||||
stageEntityChange(oldRegistrar, newRegistrar);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.tools.javascrap.BackfillRegistrarBillingAccountsCommand;
|
||||
import google.registry.tools.javascrap.CompareEscrowDepositsCommand;
|
||||
import google.registry.tools.javascrap.HardDeleteHostCommand;
|
||||
|
||||
@@ -30,6 +31,7 @@ public final class RegistryTool {
|
||||
public static final ImmutableMap<String, Class<? extends Command>> COMMAND_MAP =
|
||||
new ImmutableMap.Builder<String, Class<? extends Command>>()
|
||||
.put("ack_poll_messages", AckPollMessagesCommand.class)
|
||||
.put("backfill_registrar_billing_accounts", BackfillRegistrarBillingAccountsCommand.class)
|
||||
.put("canonicalize_labels", CanonicalizeLabelsCommand.class)
|
||||
.put("check_domain", CheckDomainCommand.class)
|
||||
.put("check_domain_claims", CheckDomainClaimsCommand.class)
|
||||
@@ -102,6 +104,7 @@ public final class RegistryTool {
|
||||
.put("registrar_contact", RegistrarContactCommand.class)
|
||||
.put("remove_registry_one_key", RemoveRegistryOneKeyCommand.class)
|
||||
.put("renew_domain", RenewDomainCommand.class)
|
||||
.put("replay_txns", ReplayTxnsCommand.class)
|
||||
.put("resave_entities", ResaveEntitiesCommand.class)
|
||||
.put("resave_environment_entities", ResaveEnvironmentEntitiesCommand.class)
|
||||
.put("resave_epp_resource", ResaveEppResourceCommand.class)
|
||||
|
||||
@@ -43,6 +43,7 @@ import google.registry.request.Modules.UrlConnectionServiceModule;
|
||||
import google.registry.request.Modules.UrlFetchServiceModule;
|
||||
import google.registry.request.Modules.UserServiceModule;
|
||||
import google.registry.tools.AuthModule.LocalCredentialModule;
|
||||
import google.registry.tools.javascrap.BackfillRegistrarBillingAccountsCommand;
|
||||
import google.registry.tools.javascrap.CompareEscrowDepositsCommand;
|
||||
import google.registry.tools.javascrap.HardDeleteHostCommand;
|
||||
import google.registry.util.UtilsModule;
|
||||
@@ -90,6 +91,8 @@ import javax.inject.Singleton;
|
||||
interface RegistryToolComponent {
|
||||
void inject(AckPollMessagesCommand command);
|
||||
|
||||
void inject(BackfillRegistrarBillingAccountsCommand command);
|
||||
|
||||
void inject(CheckDomainClaimsCommand command);
|
||||
|
||||
void inject(CheckDomainCommand command);
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// 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.tools;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaJpaTm;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.persistence.transaction.Transaction;
|
||||
import google.registry.persistence.transaction.TransactionEntity;
|
||||
import java.util.List;
|
||||
|
||||
@Parameters(separators = " =", commandDescription = "Replay a range of transactions.")
|
||||
public class ReplayTxnsCommand implements CommandWithRemoteApi {
|
||||
|
||||
private static final int BATCH_SIZE = 200;
|
||||
|
||||
@Parameter(
|
||||
names = {"-s", "--start-txn-id"},
|
||||
description = "Transaction id to start replaying at.",
|
||||
required = true)
|
||||
long startTxnId;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
List<TransactionEntity> txns;
|
||||
do {
|
||||
txns =
|
||||
replicaJpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
replicaJpaTm()
|
||||
.query(
|
||||
"SELECT txn FROM TransactionEntity txn where id >= :startTxn ORDER"
|
||||
+ " BY id",
|
||||
TransactionEntity.class)
|
||||
.setParameter("startTxn", startTxnId)
|
||||
.setMaxResults(BATCH_SIZE)
|
||||
.getResultList());
|
||||
for (TransactionEntity txn : txns) {
|
||||
System.out.println("Replaying transaction " + txn.getId());
|
||||
Transaction.deserialize(txn.getContents());
|
||||
startTxnId = txn.getId() + 1;
|
||||
}
|
||||
} while (txns.size() > 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.tools.javascrap;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.model.OteAccountBuilder.DEFAULT_BILLING_ACCOUNT_MAP;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.tools.CommandWithRemoteApi;
|
||||
|
||||
/**
|
||||
* Backfills the billing account maps on all Registrars that don't have any set.
|
||||
*
|
||||
* <p>This should not (and cannot) be used on production. Its purpose is to backfill these values on
|
||||
* sandbox, where most registrars have an empty billing account map, including all that were created
|
||||
* for OT&E. The same default billing account map is used for all registrars, and includes
|
||||
* values for USD and JPY. The actual values here don't matter as we don't do invoicing on sandbox
|
||||
* anyway.
|
||||
*/
|
||||
@Parameters(separators = " =", commandDescription = "Backfill registrar billing account maps.")
|
||||
public class BackfillRegistrarBillingAccountsCommand implements CommandWithRemoteApi {
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
checkState(
|
||||
RegistryEnvironment.get() != RegistryEnvironment.PRODUCTION,
|
||||
"Do not run this on production");
|
||||
System.out.println("Populating billing account maps on all registrars missing them ...");
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().loadAllOfStream(Registrar.class)
|
||||
.filter(r -> r.getBillingAccountMap().isEmpty())
|
||||
.forEach(
|
||||
r ->
|
||||
tm().update(
|
||||
r.asBuilder()
|
||||
.setBillingAccountMap(DEFAULT_BILLING_ACCOUNT_MAP)
|
||||
.build())));
|
||||
System.out.println("Done!");
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -22,17 +22,19 @@ import static google.registry.testing.DatabaseHelper.newRegistry;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistNewRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.LogsSubject.assertAboutLogs;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static java.util.logging.Level.SEVERE;
|
||||
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;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.testing.TestLogHandler;
|
||||
import google.registry.beam.TestPipelineExtension;
|
||||
import google.registry.model.billing.BillingEvent.Cancellation;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
@@ -59,7 +61,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 java.util.logging.Logger;
|
||||
import org.apache.beam.sdk.coders.SerializableCoder;
|
||||
import org.apache.beam.sdk.options.PipelineOptionsFactory;
|
||||
import org.apache.beam.sdk.testing.PAssert;
|
||||
@@ -252,9 +254,14 @@ class InvoicingPipelineTest {
|
||||
|
||||
private File billingBucketUrl;
|
||||
private PCollection<BillingEvent> billingEvents;
|
||||
private final TestLogHandler logHandler = new TestLogHandler();
|
||||
|
||||
private final Logger loggerToIntercept =
|
||||
Logger.getLogger(InvoicingPipeline.class.getCanonicalName());
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
loggerToIntercept.addHandler(logHandler);
|
||||
billingBucketUrl = Files.createDirectory(tmpDir.resolve(BILLING_BUCKET_URL)).toFile();
|
||||
options.setBillingBucketUrl(billingBucketUrl.getAbsolutePath());
|
||||
options.setYearMonth(YEAR_MONTH);
|
||||
@@ -300,8 +307,9 @@ class InvoicingPipelineTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_readFromCloudSqlMissingPAK() throws Exception {
|
||||
Registrar registrar = persistNewRegistrar("TheRegistrar");
|
||||
void testSuccess_readFromCloudSqlMissingPAK() throws Exception {
|
||||
setupCloudSql();
|
||||
Registrar registrar = persistNewRegistrar("ARegistrar");
|
||||
registrar =
|
||||
registrar
|
||||
.asBuilder()
|
||||
@@ -317,17 +325,16 @@ class InvoicingPipelineTest {
|
||||
persistResource(test);
|
||||
DomainBase domain = persistActiveDomain("mycanadiandomain.test");
|
||||
|
||||
persistOneTimeBillingEvent(1, domain, registrar, Reason.RENEW, 3, Money.of(CAD, 20.5));
|
||||
persistOneTimeBillingEvent(25, 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");
|
||||
PAssert.that(billingEvents).containsInAnyOrder(INPUT_EVENTS);
|
||||
pipeline.run().waitUntilFinish();
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(
|
||||
SEVERE,
|
||||
"Registrar ARegistrar does not have a product account key for the currency unit: CAD");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -336,7 +336,11 @@ public class SyncRegistrarsSheetTest {
|
||||
|
||||
@TestOfyAndSql
|
||||
void testRun_missingValues_stillWorks() throws Exception {
|
||||
persistNewRegistrar("SomeRegistrar", "Some Registrar", Registrar.Type.REAL, 8L);
|
||||
persistResource(
|
||||
persistNewRegistrar("SomeRegistrar", "Some Registrar", Registrar.Type.REAL, 8L)
|
||||
.asBuilder()
|
||||
.setBillingAccountMap(ImmutableMap.of())
|
||||
.build());
|
||||
|
||||
newSyncRegistrarsSheet().run("foobar");
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -225,7 +225,9 @@ public final class OteAccountBuilderTest {
|
||||
|
||||
OteAccountBuilder oteSetupHelper = OteAccountBuilder.forRegistrarId("myclientid");
|
||||
|
||||
assertThat(assertThrows(IllegalStateException.class, () -> oteSetupHelper.buildAndPersist()))
|
||||
IllegalStateException thrown =
|
||||
assertThrows(IllegalStateException.class, () -> oteSetupHelper.buildAndPersist());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("Found existing object(s) conflicting with OT&E objects");
|
||||
}
|
||||
@@ -236,7 +238,9 @@ public final class OteAccountBuilderTest {
|
||||
|
||||
OteAccountBuilder oteSetupHelper = OteAccountBuilder.forRegistrarId("myclientid");
|
||||
|
||||
assertThat(assertThrows(IllegalStateException.class, () -> oteSetupHelper.buildAndPersist()))
|
||||
IllegalStateException thrown =
|
||||
assertThrows(IllegalStateException.class, () -> oteSetupHelper.buildAndPersist());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("Found existing object(s) conflicting with OT&E objects");
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,10 +131,11 @@ public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
|
||||
.put(startTime.plusHours(2), DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.put(startTime.plusHours(3), DATASTORE_PRIMARY_READ_ONLY)
|
||||
.build();
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> jpaTm().transact(() -> DatabaseMigrationStateSchedule.set(nowInvalidMap))))
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> jpaTm().transact(() -> DatabaseMigrationStateSchedule.set(nowInvalidMap)));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Cannot transition from current state-as-of-now DATASTORE_ONLY "
|
||||
@@ -143,14 +144,13 @@ public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
|
||||
|
||||
@Test
|
||||
void testFailure_notInTransaction() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
DatabaseMigrationStateSchedule.DEFAULT_TRANSITION_MAP.toValueMap())))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Not in a transaction");
|
||||
IllegalStateException thrown =
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
DatabaseMigrationStateSchedule.DEFAULT_TRANSITION_MAP.toValueMap()));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Not in a transaction");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -188,10 +188,11 @@ public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
|
||||
private void runInvalidTransition(MigrationState from, MigrationState to) {
|
||||
ImmutableSortedMap<DateTime, MigrationState> transitions =
|
||||
createMapEndingWithTransition(from, to);
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> jpaTm().transact(() -> DatabaseMigrationStateSchedule.set(transitions))))
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> jpaTm().transact(() -> DatabaseMigrationStateSchedule.set(transitions)));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
String.format("validStateTransitions map cannot transition from %s to %s.", from, to));
|
||||
|
||||
@@ -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,48 @@ 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);
|
||||
persistResource(
|
||||
loadedToken.asBuilder().setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED).build());
|
||||
assertThat(loadByEntity(token).getRenewalPriceBehavior())
|
||||
.isEqualTo(RenewalPriceBehavior.SPECIFIED);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSetCreationTime_cantCallMoreThanOnce() {
|
||||
AllocationToken.Builder builder =
|
||||
|
||||
@@ -30,11 +30,14 @@ import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResources;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
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;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.config.RegistryConfig;
|
||||
@@ -42,13 +45,17 @@ import google.registry.model.EntityTestCase;
|
||||
import google.registry.model.registrar.Registrar.State;
|
||||
import google.registry.model.registrar.Registrar.Type;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import google.registry.testing.TestOfyOnly;
|
||||
import google.registry.testing.TestSqlOnly;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import google.registry.util.SerializeUtils;
|
||||
import java.math.BigDecimal;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
/** Unit tests for {@link Registrar}. */
|
||||
@@ -60,7 +67,20 @@ class RegistrarTest extends EntityTestCase {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
createTld("xn--q9jyb4c");
|
||||
createTld("tld");
|
||||
persistResource(
|
||||
newRegistry("xn--q9jyb4c", "MINNA")
|
||||
.asBuilder()
|
||||
.setCurrency(JPY)
|
||||
.setCreateBillingCost(Money.of(JPY, new BigDecimal(1300)))
|
||||
.setRestoreBillingCost(Money.of(JPY, new BigDecimal(1700)))
|
||||
.setServerStatusChangeBillingCost(Money.of(JPY, new BigDecimal(1900)))
|
||||
.setRegistryLockOrUnlockBillingCost(Money.of(JPY, new BigDecimal(2700)))
|
||||
.setRenewBillingCostTransitions(
|
||||
ImmutableSortedMap.of(START_OF_TIME, Money.of(JPY, new BigDecimal(1100))))
|
||||
.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(JPY)))
|
||||
.setPremiumList(null)
|
||||
.build());
|
||||
// Set up a new persisted registrar entity.
|
||||
registrar =
|
||||
cloneAndSetAutoTimestamps(
|
||||
@@ -251,8 +271,10 @@ class RegistrarTest extends EntityTestCase {
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_clearingBillingAccountMap() {
|
||||
registrar = registrar.asBuilder().setBillingAccountMap(null).build();
|
||||
void testSuccess_clearingBillingAccountMapAndAllowedTlds() {
|
||||
registrar =
|
||||
registrar.asBuilder().setAllowedTlds(ImmutableSet.of()).setBillingAccountMap(null).build();
|
||||
assertThat(registrar.getAllowedTlds()).isEmpty();
|
||||
assertThat(registrar.getBillingAccountMap()).isEmpty();
|
||||
}
|
||||
|
||||
@@ -677,4 +699,48 @@ class RegistrarTest extends EntityTestCase {
|
||||
assertThrows(IllegalArgumentException.class, () -> Registrar.loadByRegistrarIdCached(""));
|
||||
assertThat(thrown).hasMessageThat().contains("registrarId must be specified");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_missingCurrenciesFromBillingMap() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
registrar
|
||||
.asBuilder()
|
||||
.setBillingAccountMap(null)
|
||||
.setAllowedTlds(ImmutableSet.of("tld", "xn--q9jyb4c"))
|
||||
.build());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("their currency is missing from the billing account map: [tld, xn--q9jyb4c]");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_missingCurrencyFromBillingMap() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
registrar
|
||||
.asBuilder()
|
||||
.setBillingAccountMap(ImmutableMap.of(USD, "abc123"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld", "xn--q9jyb4c"))
|
||||
.build());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("their currency is missing from the billing account map: [xn--q9jyb4c]");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_nonRealTldDoesntNeedEntryInBillingMap() {
|
||||
persistResource(Registry.get("xn--q9jyb4c").asBuilder().setTldType(TldType.TEST).build());
|
||||
// xn--q9jyb4c bills in JPY and we don't have a JPY entry in this billing account map, but it
|
||||
// should succeed without throwing an error because xn--q9jyb4c is set to be a TEST TLD.
|
||||
registrar
|
||||
.asBuilder()
|
||||
.setBillingAccountMap(ImmutableMap.of(USD, "abc123"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld", "xn--q9jyb4c"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,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
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.model.tld;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.testing.SqlHelper.getMostRecentRegistryLockByRepoId;
|
||||
import static google.registry.testing.SqlHelper.getMostRecentUnlockedRegistryLockByRepoId;
|
||||
@@ -67,7 +68,7 @@ public final class RegistryLockDaoTest extends EntityTestCase {
|
||||
() -> {
|
||||
RegistryLock fromDatabase =
|
||||
RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get();
|
||||
assertThat(fromDatabase.getLockCompletionTime().get()).isEqualTo(fakeClock.nowUtc());
|
||||
assertThat(fromDatabase.getLockCompletionTime()).hasValue(fakeClock.nowUtc());
|
||||
assertThat(fromDatabase.getLastUpdateTime()).isEqualTo(fakeClock.nowUtc());
|
||||
});
|
||||
}
|
||||
@@ -87,7 +88,7 @@ public final class RegistryLockDaoTest extends EntityTestCase {
|
||||
assertThat(fromDatabase.getUnlockRequestTime()).isEqualTo(Optional.of(fakeClock.nowUtc()));
|
||||
assertThat(fromDatabase.getUnlockCompletionTime()).isEqualTo(Optional.of(fakeClock.nowUtc()));
|
||||
assertThat(fromDatabase.isLocked()).isFalse();
|
||||
assertThat(fromDatabase.getRelockDuration().get()).isEqualTo(Duration.standardHours(6));
|
||||
assertThat(fromDatabase.getRelockDuration()).hasValue(Duration.standardHours(6));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -190,7 +190,7 @@ public final class RegistryTest extends EntityTestCase {
|
||||
@TestOfyAndSql
|
||||
void testGetAll() {
|
||||
createTld("foo");
|
||||
assertThat(Registry.getAll(ImmutableSet.of("foo", "tld")))
|
||||
assertThat(Registry.get(ImmutableSet.of("foo", "tld")))
|
||||
.containsExactlyElementsIn(
|
||||
tm().transact(
|
||||
() ->
|
||||
@@ -261,8 +261,8 @@ public final class RegistryTest extends EntityTestCase {
|
||||
Registry registry = Registry.get("tld").asBuilder().setPremiumList(pl2).build();
|
||||
Optional<String> pl = registry.getPremiumListName();
|
||||
assertThat(pl).hasValue("tld2");
|
||||
PremiumList stored = PremiumListDao.getLatestRevision(pl.get()).get();
|
||||
assertThat(stored.getName()).isEqualTo("tld2");
|
||||
assertThat(PremiumListDao.getLatestRevision("tld2")).isPresent();
|
||||
assertThat(PremiumListDao.getLatestRevision("tld2").get().getName()).isEqualTo("tld2");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
|
||||
@@ -261,7 +261,7 @@ public class PremiumListDaoTest {
|
||||
assertThat(PremiumListDao.premiumListCache.getIfPresent("testname")).isNull();
|
||||
PremiumListDao.save(testList);
|
||||
PremiumList pl = PremiumListDao.getLatestRevision("testname").get();
|
||||
assertThat(PremiumListDao.premiumListCache.getIfPresent("testname").get()).isEqualTo(pl);
|
||||
assertThat(PremiumListDao.premiumListCache.getIfPresent("testname")).hasValue(pl);
|
||||
TransactionManagerUtil.transactIfJpaTm(
|
||||
() -> PremiumListDao.save("testname", USD, ImmutableList.of("test,USD 1")));
|
||||
assertThat(PremiumListDao.premiumListCache.getIfPresent("testname")).isNull();
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.model.tld.label;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||
import static google.registry.testing.DatabaseHelper.persistReservedList;
|
||||
@@ -100,8 +101,8 @@ public class PremiumListTest {
|
||||
@Test
|
||||
void testParse_canIncludeOrNotIncludeCurrencyUnit() {
|
||||
PremiumListDao.save("tld", USD, ImmutableList.of("rofl,USD 90", "paper, 80"));
|
||||
assertThat(PremiumListDao.getPremiumPrice("tld", "rofl").get()).isEqualTo(Money.of(USD, 90));
|
||||
assertThat(PremiumListDao.getPremiumPrice("tld", "paper").get()).isEqualTo(Money.of(USD, 80));
|
||||
assertThat(PremiumListDao.getPremiumPrice("tld", "rofl")).hasValue(Money.of(USD, 90));
|
||||
assertThat(PremiumListDao.getPremiumPrice("tld", "paper")).hasValue(Money.of(USD, 80));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
package google.registry.model.tmch;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
|
||||
import google.registry.model.EntityTestCase;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link TmchCrl}. */
|
||||
@@ -29,7 +29,7 @@ public class TmchCrlTest extends EntityTestCase {
|
||||
|
||||
@Test
|
||||
void testSuccess() {
|
||||
assertThat(TmchCrl.get()).isEqualTo(Optional.empty());
|
||||
assertThat(TmchCrl.get()).isEmpty();
|
||||
TmchCrl.set("lolcat", "https://lol.cat");
|
||||
assertThat(TmchCrl.get().get().getCrl()).isEqualTo("lolcat");
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -73,8 +73,10 @@ final class AbstractJsonableObjectTest {
|
||||
@JsonableElement String myString = "A";
|
||||
@JsonableElement("myString") String anotherString = "B";
|
||||
};
|
||||
assertThat(assertThrows(JsonableException.class, () -> jsonable.toJson()))
|
||||
.hasMessageThat().contains("Encountered the same field name 'myString' multiple times");
|
||||
JsonableException thrown = assertThrows(JsonableException.class, () -> jsonable.toJson());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("Encountered the same field name 'myString' multiple times");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -96,8 +98,8 @@ final class AbstractJsonableObjectTest {
|
||||
return in;
|
||||
}
|
||||
};
|
||||
assertThat(assertThrows(JsonableException.class, () -> jsonable.toJson()))
|
||||
.hasMessageThat().contains("must have no arguments");
|
||||
JsonableException thrown = assertThrows(JsonableException.class, () -> jsonable.toJson());
|
||||
assertThat(thrown).hasMessageThat().contains("must have no arguments");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -186,8 +188,10 @@ final class AbstractJsonableObjectTest {
|
||||
@JsonableElement("myList[]")
|
||||
Optional<Integer> myListMeaningOfLife = Optional.of(42);
|
||||
};
|
||||
assertThat(assertThrows(JsonableException.class, () -> jsonable.toJson()))
|
||||
.hasMessageThat().contains("Encountered the same field name 'myList' multiple times");
|
||||
JsonableException thrown = assertThrows(JsonableException.class, () -> jsonable.toJson());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("Encountered the same field name 'myList' multiple times");
|
||||
}
|
||||
|
||||
@RestrictJsonNames({"allowed", "allowedList[]"})
|
||||
@@ -226,7 +230,8 @@ final class AbstractJsonableObjectTest {
|
||||
@JsonableElement
|
||||
JsonableWithNameRestrictions wrong = new JsonableWithNameRestrictions();
|
||||
};
|
||||
assertThat(assertThrows(JsonableException.class, () -> jsonable.toJson()))
|
||||
JsonableException thrown = assertThrows(JsonableException.class, () -> jsonable.toJson());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("must be named one of ['allowed', 'allowedList[]'], but is named 'wrong'");
|
||||
}
|
||||
@@ -237,9 +242,8 @@ final class AbstractJsonableObjectTest {
|
||||
@JsonableElement
|
||||
JsonableWithNoAllowedNames wrong = new JsonableWithNoAllowedNames();
|
||||
};
|
||||
assertThat(assertThrows(JsonableException.class, () -> jsonable.toJson()))
|
||||
.hasMessageThat()
|
||||
.contains("is annotated with an empty RestrictJsonNames");
|
||||
JsonableException thrown = assertThrows(JsonableException.class, () -> jsonable.toJson());
|
||||
assertThat(thrown).hasMessageThat().contains("is annotated with an empty RestrictJsonNames");
|
||||
}
|
||||
|
||||
@RestrictJsonNames({})
|
||||
|
||||
@@ -232,17 +232,15 @@ public final class UpdateRegistrarRdapBaseUrlsActionTest {
|
||||
@TestOfyAndSql
|
||||
void testNoTlds() {
|
||||
deleteTld("tld");
|
||||
assertThat(assertThrows(IllegalArgumentException.class, action::run))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("There must exist at least one REAL TLD.");
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, action::run);
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("There must exist at least one REAL TLD.");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testOnlyTestTlds() {
|
||||
persistResource(Registry.get("tld").asBuilder().setTldType(TldType.TEST).build());
|
||||
assertThat(assertThrows(IllegalArgumentException.class, action::run))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("There must exist at least one REAL TLD.");
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, action::run);
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("There must exist at least one REAL TLD.");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
@@ -278,7 +276,8 @@ public final class UpdateRegistrarRdapBaseUrlsActionTest {
|
||||
httpTransport.addNextResponse(badLoginResponse);
|
||||
httpTransport.addNextResponse(badLoginResponse);
|
||||
|
||||
assertThat(assertThrows(RuntimeException.class, action::run))
|
||||
RuntimeException thrown = assertThrows(RuntimeException.class, action::run);
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Error contacting MosAPI server. Tried TLDs [secondtld, tld]");
|
||||
}
|
||||
@@ -316,9 +315,8 @@ public final class UpdateRegistrarRdapBaseUrlsActionTest {
|
||||
httpTransport.addNextResponse(logoutResponse);
|
||||
httpTransport.addNextResponse(badLoginResponse);
|
||||
|
||||
assertThat(assertThrows(RuntimeException.class, action::run))
|
||||
.hasCauseThat()
|
||||
.isInstanceOf(JsonSyntaxException.class);
|
||||
RuntimeException thrown = assertThrows(RuntimeException.class, action::run);
|
||||
assertThat(thrown).hasCauseThat().isInstanceOf(JsonSyntaxException.class);
|
||||
}
|
||||
|
||||
private static void addValidResponses(TestHttpTransport httpTransport) {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,9 +403,9 @@ class AuthenticatedRegistrarAccessorTest {
|
||||
AuthenticatedRegistrarAccessor registrarAccessor =
|
||||
AuthenticatedRegistrarAccessor.createForTesting(ImmutableSetMultimap.of());
|
||||
|
||||
assertThat(assertThrows(RegistrarAccessDeniedException.class, registrarAccessor::guessClientId))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("TestUserId isn't associated with any registrar");
|
||||
RegistrarAccessDeniedException thrown =
|
||||
assertThrows(RegistrarAccessDeniedException.class, registrarAccessor::guessClientId);
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("TestUserId isn't associated with any registrar");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
|
||||
@@ -66,6 +66,7 @@ import java.util.Set;
|
||||
import java.util.logging.LogManager;
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@@ -289,6 +290,7 @@ public final class AppEngineExtension implements BeforeEachCallback, AfterEachCa
|
||||
.build())
|
||||
.setPhoneNumber("+1.3334445555")
|
||||
.setPhonePasscode("12345")
|
||||
.setBillingAccountMap(ImmutableMap.of(CurrencyUnit.USD, "abc123"))
|
||||
.setContactsRequireSyncing(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -762,6 +762,7 @@ public class DatabaseHelper {
|
||||
.setType(type)
|
||||
.setState(State.ACTIVE)
|
||||
.setIanaIdentifier(ianaIdentifier)
|
||||
.setBillingAccountMap(ImmutableMap.of(USD, "abc123"))
|
||||
.setLocalizedAddress(
|
||||
new RegistrarAddress.Builder()
|
||||
.setStreet(ImmutableList.of("123 Fake St"))
|
||||
|
||||
@@ -61,41 +61,38 @@ abstract class CreateOrUpdateReservedListCommandTestCase<
|
||||
|
||||
@Test
|
||||
void testFailure_fileDoesntExist() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
ParameterException.class,
|
||||
() ->
|
||||
runCommandForced(
|
||||
"--name=xn--q9jyb4c_common-reserved",
|
||||
"--input=" + reservedTermsPath + "-nonexistent")))
|
||||
.hasMessageThat()
|
||||
.contains("-i not found");
|
||||
ParameterException thrown =
|
||||
assertThrows(
|
||||
ParameterException.class,
|
||||
() ->
|
||||
runCommandForced(
|
||||
"--name=xn--q9jyb4c_common-reserved",
|
||||
"--input=" + reservedTermsPath + "-nonexistent"));
|
||||
assertThat(thrown).hasMessageThat().contains("-i not found");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_fileDoesntParse() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
runCommandForced(
|
||||
"--name=xn--q9jyb4c_common-reserved",
|
||||
"--input=" + invalidReservedTermsPath)))
|
||||
.hasMessageThat()
|
||||
.contains("No enum constant");
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
runCommandForced(
|
||||
"--name=xn--q9jyb4c_common-reserved", "--input=" + invalidReservedTermsPath));
|
||||
assertThat(thrown).hasMessageThat().contains("No enum constant");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_invalidLabel_includesFullDomainName() throws Exception {
|
||||
Files.asCharSink(new File(invalidReservedTermsPath), UTF_8)
|
||||
.write("example.tld,FULLY_BLOCKED\n\n");
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
runCommandForced(
|
||||
"--name=xn--q9jyb4c_common-reserved",
|
||||
"--input=" + invalidReservedTermsPath)))
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
runCommandForced(
|
||||
"--name=xn--q9jyb4c_common-reserved", "--input=" + invalidReservedTermsPath));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Label example.tld must not be a multi-level domain name");
|
||||
}
|
||||
|
||||
@@ -21,8 +21,11 @@ import static google.registry.testing.CertificateSamples.SAMPLE_CERT;
|
||||
import static google.registry.testing.CertificateSamples.SAMPLE_CERT3;
|
||||
import static google.registry.testing.CertificateSamples.SAMPLE_CERT3_HASH;
|
||||
import static google.registry.testing.DatabaseHelper.createTlds;
|
||||
import static google.registry.testing.DatabaseHelper.newRegistry;
|
||||
import static google.registry.testing.DatabaseHelper.persistNewRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.JPY;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -43,8 +46,10 @@ import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
@@ -604,11 +609,11 @@ class CreateRegistrarCommandTest extends CommandTestCase<CreateRegistrarCommand>
|
||||
Optional<Registrar> registrar = Registrar.loadByRegistrarId("clientz");
|
||||
assertThat(registrar).isPresent();
|
||||
assertThat(registrar.get().getBillingAccountMap())
|
||||
.containsExactly(CurrencyUnit.USD, "abc123", CurrencyUnit.JPY, "789xyz");
|
||||
.containsExactly(CurrencyUnit.USD, "abc123", JPY, "789xyz");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_billingAccountMap_doesNotContainEntryForTldAllowed() {
|
||||
void testFailure_billingAccountMap_doesNotContainEntryForAllowedTld() {
|
||||
createTlds("foo");
|
||||
|
||||
IllegalArgumentException thrown =
|
||||
@@ -632,12 +637,26 @@ class CreateRegistrarCommandTest extends CommandTestCase<CreateRegistrarCommand>
|
||||
"--cc US",
|
||||
"--force",
|
||||
"clientz"));
|
||||
assertThat(thrown).hasMessageThat().contains("USD");
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("their currency is missing from the billing account map: [foo]");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testSuccess_billingAccountMap_onlyAppliesToRealRegistrar() throws Exception {
|
||||
createTlds("foo");
|
||||
persistResource(
|
||||
newRegistry("foo", "FOO")
|
||||
.asBuilder()
|
||||
.setCurrency(JPY)
|
||||
.setCreateBillingCost(Money.of(JPY, new BigDecimal(1300)))
|
||||
.setRestoreBillingCost(Money.of(JPY, new BigDecimal(1700)))
|
||||
.setServerStatusChangeBillingCost(Money.of(JPY, new BigDecimal(1900)))
|
||||
.setRegistryLockOrUnlockBillingCost(Money.of(JPY, new BigDecimal(2700)))
|
||||
.setRenewBillingCostTransitions(
|
||||
ImmutableSortedMap.of(START_OF_TIME, Money.of(JPY, new BigDecimal(1100))))
|
||||
.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(JPY)))
|
||||
.setPremiumList(null)
|
||||
.build());
|
||||
|
||||
runCommandForced(
|
||||
"--name=blobio",
|
||||
@@ -656,7 +675,7 @@ class CreateRegistrarCommandTest extends CommandTestCase<CreateRegistrarCommand>
|
||||
|
||||
Optional<Registrar> registrar = Registrar.loadByRegistrarId("clientz");
|
||||
assertThat(registrar).isPresent();
|
||||
assertThat(registrar.get().getBillingAccountMap()).containsExactly(CurrencyUnit.JPY, "789xyz");
|
||||
assertThat(registrar.get().getBillingAccountMap()).containsExactly(JPY, "789xyz");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
|
||||
@@ -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;
|
||||
@@ -59,7 +60,6 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.junit.Assert;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
@@ -316,12 +316,13 @@ public final class DomainLockUtilsTest {
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.empty());
|
||||
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.empty())))
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.empty()));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("A pending unlock action already exists for example.tld");
|
||||
}
|
||||
@@ -331,37 +332,38 @@ public final class DomainLockUtilsTest {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true);
|
||||
domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true);
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.empty())))
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.empty()));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Non-admin user cannot unlock admin-locked domain example.tld");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_createLock_unknownDomain() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainLockUtils.saveNewRegistryLockRequest(
|
||||
"asdf.tld", "TheRegistrar", POC_ID, false)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Domain doesn't exist");
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainLockUtils.saveNewRegistryLockRequest(
|
||||
"asdf.tld", "TheRegistrar", POC_ID, false));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Domain doesn't exist");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_createLock_alreadyPendingLock() {
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false);
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainLockUtils.saveNewRegistryLockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", POC_ID, false)))
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainLockUtils.saveNewRegistryLockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", POC_ID, false));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("A pending or completed lock action already exists for example.tld");
|
||||
}
|
||||
@@ -369,26 +371,24 @@ public final class DomainLockUtilsTest {
|
||||
@TestOfyAndSql
|
||||
void testFailure_createLock_alreadyLocked() {
|
||||
persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainLockUtils.saveNewRegistryLockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", POC_ID, false)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Domain example.tld is already locked");
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainLockUtils.saveNewRegistryLockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", POC_ID, false));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Domain example.tld is already locked");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_createUnlock_alreadyUnlocked() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.empty())))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Domain example.tld is already unlocked");
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.empty()));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Domain example.tld is already unlocked");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
@@ -397,12 +397,11 @@ public final class DomainLockUtilsTest {
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false);
|
||||
domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false);
|
||||
domain = loadByEntity(domain);
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Domain example.tld is already locked");
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Domain example.tld is already locked");
|
||||
assertNoDomainChanges();
|
||||
}
|
||||
|
||||
@@ -411,12 +410,11 @@ public final class DomainLockUtilsTest {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false);
|
||||
clock.advanceBy(standardDays(1));
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("The pending lock has expired; please try again");
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("The pending lock has expired; please try again");
|
||||
assertNoDomainChanges();
|
||||
}
|
||||
|
||||
@@ -424,12 +422,11 @@ public final class DomainLockUtilsTest {
|
||||
void testFailure_applyLock_nonAdmin_applyAdminLock() {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true);
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Non-admin user cannot complete admin lock");
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Non-admin user cannot complete admin lock");
|
||||
assertNoDomainChanges();
|
||||
}
|
||||
|
||||
@@ -443,12 +440,11 @@ public final class DomainLockUtilsTest {
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.empty());
|
||||
domainLockUtils.verifyAndApplyUnlock(unlock.getVerificationCode(), false);
|
||||
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.verifyAndApplyUnlock(unlock.getVerificationCode(), false)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Domain example.tld is already unlocked");
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.verifyAndApplyUnlock(unlock.getVerificationCode(), false));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Domain example.tld is already unlocked");
|
||||
assertNoDomainChanges();
|
||||
}
|
||||
|
||||
@@ -460,12 +456,11 @@ public final class DomainLockUtilsTest {
|
||||
// reload to pick up modification times, etc
|
||||
lock = getRegistryLockByVerificationCode(verificationCode).get();
|
||||
domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.verifyAndApplyLock(verificationCode, false)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Domain example.tld is already locked");
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.verifyAndApplyLock(verificationCode, false));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Domain example.tld is already locked");
|
||||
|
||||
// Failure during Datastore portion shouldn't affect the SQL object
|
||||
RegistryLock afterAction = getRegistryLockByVerificationCode(lock.getVerificationCode()).get();
|
||||
@@ -516,10 +511,11 @@ public final class DomainLockUtilsTest {
|
||||
.setRegistrarPocId("someone@example.com")
|
||||
.setVerificationCode("hi")
|
||||
.build());
|
||||
assertThat(
|
||||
Assert.assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.enqueueDomainRelock(lockWithoutDuration)))
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.enqueueDomainRelock(lockWithoutDuration));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
String.format(
|
||||
@@ -546,7 +542,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);
|
||||
|
||||
@@ -133,7 +133,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
runCommand("--prefix", "ooo", "--number", "100", "--length", "16");
|
||||
// The deterministic string generator makes it too much hassle to assert about each token, so
|
||||
// just assert total number.
|
||||
assertThat(loadAllOf(AllocationToken.class).size()).isEqualTo(100);
|
||||
assertThat(loadAllOf(AllocationToken.class)).hasSize(100);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
@@ -200,7 +200,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
Collection<String> sampleTokens = command.stringGenerator.createStrings(13, 100);
|
||||
runCommand("--tokens", Joiner.on(",").join(sampleTokens));
|
||||
assertInStdout(Iterables.toArray(sampleTokens, String.class));
|
||||
assertThat(loadAllOf(AllocationToken.class).size()).isEqualTo(100);
|
||||
assertThat(loadAllOf(AllocationToken.class)).hasSize(100);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
|
||||
@@ -139,14 +139,15 @@ public class SetDatabaseMigrationStateCommandTest
|
||||
|
||||
@TestOfyAndSql
|
||||
void testFailure_invalidTransition() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
runCommandForced(
|
||||
String.format(
|
||||
"--migration_schedule=%s=DATASTORE_ONLY,%s=DATASTORE_PRIMARY_READ_ONLY",
|
||||
START_OF_TIME, START_OF_TIME.plusHours(1)))))
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
runCommandForced(
|
||||
String.format(
|
||||
"--migration_schedule=%s=DATASTORE_ONLY,%s=DATASTORE_PRIMARY_READ_ONLY",
|
||||
START_OF_TIME, START_OF_TIME.plusHours(1))));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"validStateTransitions map cannot transition from DATASTORE_ONLY "
|
||||
@@ -158,18 +159,16 @@ public class SetDatabaseMigrationStateCommandTest
|
||||
// The map we pass in is valid by itself, but we can't go from DATASTORE_ONLY now to
|
||||
// DATASTORE_PRIMARY_READ_ONLY now
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
runCommandForced(
|
||||
String.format(
|
||||
"--migration_schedule=%s=DATASTORE_ONLY,%s=DATASTORE_PRIMARY,"
|
||||
+ "%s=DATASTORE_PRIMARY_NO_ASYNC,%s=DATASTORE_PRIMARY_READ_ONLY",
|
||||
START_OF_TIME,
|
||||
now.minusHours(3),
|
||||
now.minusHours(2),
|
||||
now.minusHours(1)))))
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
runCommandForced(
|
||||
String.format(
|
||||
"--migration_schedule=%s=DATASTORE_ONLY,%s=DATASTORE_PRIMARY,"
|
||||
+ "%s=DATASTORE_PRIMARY_NO_ASYNC,%s=DATASTORE_PRIMARY_READ_ONLY",
|
||||
START_OF_TIME, now.minusHours(3), now.minusHours(2), now.minusHours(1))));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Cannot transition from current state-as-of-now DATASTORE_ONLY "
|
||||
|
||||
@@ -21,8 +21,10 @@ import static google.registry.testing.CertificateSamples.SAMPLE_CERT3;
|
||||
import static google.registry.testing.CertificateSamples.SAMPLE_CERT3_HASH;
|
||||
import static google.registry.testing.DatabaseHelper.createTlds;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.newRegistry;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.JPY;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@@ -38,8 +40,10 @@ import google.registry.model.registrar.Registrar.State;
|
||||
import google.registry.model.registrar.Registrar.Type;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -359,6 +363,8 @@ class UpdateRegistrarCommandTest extends CommandTestCase<UpdateRegistrarCommand>
|
||||
|
||||
@Test
|
||||
void testSuccess_billingAccountMap() throws Exception {
|
||||
persistResource(
|
||||
loadRegistrar("NewRegistrar").asBuilder().setBillingAccountMap(ImmutableMap.of()).build());
|
||||
assertThat(loadRegistrar("NewRegistrar").getBillingAccountMap()).isEmpty();
|
||||
runCommand("--billing_account_map=USD=abc123,JPY=789xyz", "--force", "NewRegistrar");
|
||||
assertThat(loadRegistrar("NewRegistrar").getBillingAccountMap())
|
||||
@@ -366,8 +372,14 @@ class UpdateRegistrarCommandTest extends CommandTestCase<UpdateRegistrarCommand>
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_billingAccountMap_doesNotContainEntryForTldAllowed() {
|
||||
void testFailure_billingAccountMap_doesNotContainEntryForAllowedTld() {
|
||||
createTlds("foo");
|
||||
persistResource(
|
||||
loadRegistrar("NewRegistrar")
|
||||
.asBuilder()
|
||||
.setAllowedTlds(ImmutableSet.of())
|
||||
.setBillingAccountMap(ImmutableMap.of())
|
||||
.build());
|
||||
assertThat(loadRegistrar("NewRegistrar").getBillingAccountMap()).isEmpty();
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -379,12 +391,28 @@ class UpdateRegistrarCommandTest extends CommandTestCase<UpdateRegistrarCommand>
|
||||
"--force",
|
||||
"--registrar_type=REAL",
|
||||
"NewRegistrar"));
|
||||
assertThat(thrown).hasMessageThat().contains("USD");
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("their currency is missing from the billing account map: [foo]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_billingAccountMap_onlyAppliesToRealRegistrar() throws Exception {
|
||||
createTlds("foo");
|
||||
persistResource(
|
||||
newRegistry("foo", "FOO")
|
||||
.asBuilder()
|
||||
.setCurrency(JPY)
|
||||
.setCreateBillingCost(Money.of(JPY, new BigDecimal(1300)))
|
||||
.setRestoreBillingCost(Money.of(JPY, new BigDecimal(1700)))
|
||||
.setServerStatusChangeBillingCost(Money.of(JPY, new BigDecimal(1900)))
|
||||
.setRegistryLockOrUnlockBillingCost(Money.of(JPY, new BigDecimal(2700)))
|
||||
.setRenewBillingCostTransitions(
|
||||
ImmutableSortedMap.of(START_OF_TIME, Money.of(JPY, new BigDecimal(1100))))
|
||||
.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(JPY)))
|
||||
.setPremiumList(null)
|
||||
.build());
|
||||
persistResource(
|
||||
loadRegistrar("NewRegistrar").asBuilder().setBillingAccountMap(ImmutableMap.of()).build());
|
||||
assertThat(loadRegistrar("NewRegistrar").getBillingAccountMap()).isEmpty();
|
||||
runCommand("--billing_account_map=JPY=789xyz", "--allowed_tlds=foo", "--force", "NewRegistrar");
|
||||
assertThat(loadRegistrar("NewRegistrar").getBillingAccountMap())
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user