mirror of
https://github.com/google/nomulus
synced 2026-06-09 16:33:02 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 30d57d9476 | |||
| 700d612ff9 | |||
| e3d400958c | |||
| aa84d5d138 | |||
| d685f7e2df | |||
| 40eef2a06c | |||
| 8bd5eb4eca | |||
| 83918e92b5 | |||
| 5bba65835a | |||
| 1e51f51979 | |||
| db2e896d42 | |||
| 478064f32b | |||
| 0db535b838 | |||
| 3705f37fab | |||
| 86bdd154bc |
+2
-1
@@ -220,6 +220,7 @@ dependencies {
|
||||
compile deps['com.googlecode.json-simple:json-simple']
|
||||
compile deps['com.jcraft:jsch']
|
||||
testCompile deps['com.thoughtworks.qdox:qdox']
|
||||
compile deps['com.zaxxer:HikariCP']
|
||||
compile deps['dnsjava:dnsjava']
|
||||
runtime deps['guru.nidi:graphviz-java-all-j2v8']
|
||||
testCompile deps['io.github.classgraph:classgraph']
|
||||
@@ -257,6 +258,7 @@ dependencies {
|
||||
testCompile deps['com.fasterxml.jackson.core:jackson-databind']
|
||||
runtime deps['org.glassfish.jaxb:jaxb-runtime']
|
||||
compile deps['org.hibernate:hibernate-core']
|
||||
compile deps['org.hibernate:hibernate-hikaricp']
|
||||
compile deps['org.joda:joda-money']
|
||||
compile deps['org.json:json']
|
||||
compile deps['org.jsoup:jsoup']
|
||||
@@ -310,7 +312,6 @@ dependencies {
|
||||
testCompile deps['org.hamcrest:hamcrest-all']
|
||||
testCompile deps['org.hamcrest:hamcrest-core']
|
||||
testCompile deps['org.hamcrest:hamcrest-library']
|
||||
compile deps['org.hibernate:hibernate-hikaricp']
|
||||
testCompile deps['junit:junit']
|
||||
testCompile deps['org.junit.jupiter:junit-jupiter-api']
|
||||
testCompile deps['org.junit.jupiter:junit-jupiter-engine']
|
||||
|
||||
@@ -125,7 +125,7 @@ com.squareup.okio:okio:1.13.0
|
||||
com.sun.istack:istack-commons-runtime:3.0.7
|
||||
com.sun.xml.fastinfoset:FastInfoset:1.2.15
|
||||
com.thoughtworks.paranamer:paranamer:2.7
|
||||
com.zaxxer:HikariCP:3.2.0
|
||||
com.zaxxer:HikariCP:3.4.5
|
||||
commons-codec:commons-codec:1.13
|
||||
commons-logging:commons-logging:1.2
|
||||
dnsjava:dnsjava:2.1.7
|
||||
@@ -176,7 +176,7 @@ javax.xml.bind:jaxb-api:2.3.1
|
||||
jline:jline:1.0
|
||||
joda-time:joda-time:2.10.5
|
||||
junit:junit:4.12
|
||||
net.bytebuddy:byte-buddy:1.10.10
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.java.dev.jna:jna-platform:5.5.0
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
@@ -205,7 +205,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
|
||||
org.bouncycastle:bcpkix-jdk15on:1.61
|
||||
org.bouncycastle:bcprov-jdk15on:1.61
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.11.1
|
||||
org.checkerframework:checker-qual:3.5.0
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18
|
||||
@@ -216,11 +216,11 @@ org.flywaydb:flyway-core:5.2.4
|
||||
org.glassfish.jaxb:jaxb-runtime:2.3.1
|
||||
org.glassfish.jaxb:txw2:2.3.1
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
|
||||
org.hibernate:hibernate-core:5.4.17.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.17.Final
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
|
||||
org.hibernate:hibernate-core:5.4.23.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.23.Final
|
||||
org.javassist:javassist:3.24.0-GA
|
||||
org.jboss.logging:jboss-logging:3.3.2.Final
|
||||
org.jboss.logging:jboss-logging:3.4.1.Final
|
||||
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
|
||||
org.jboss:jandex:2.1.3.Final
|
||||
org.jetbrains:annotations:19.0.0
|
||||
@@ -236,7 +236,7 @@ org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -124,7 +124,7 @@ com.squareup.okio:okio:1.13.0
|
||||
com.sun.istack:istack-commons-runtime:3.0.7
|
||||
com.sun.xml.fastinfoset:FastInfoset:1.2.15
|
||||
com.thoughtworks.paranamer:paranamer:2.7
|
||||
com.zaxxer:HikariCP:3.2.0
|
||||
com.zaxxer:HikariCP:3.4.5
|
||||
commons-codec:commons-codec:1.13
|
||||
commons-logging:commons-logging:1.2
|
||||
dnsjava:dnsjava:2.1.7
|
||||
@@ -174,7 +174,7 @@ javax.validation:validation-api:1.0.0.GA
|
||||
javax.xml.bind:jaxb-api:2.3.1
|
||||
jline:jline:1.0
|
||||
joda-time:joda-time:2.10.5
|
||||
net.bytebuddy:byte-buddy:1.10.10
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.java.dev.jna:jna-platform:5.5.0
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
@@ -213,11 +213,11 @@ org.easymock:easymock:3.0
|
||||
org.flywaydb:flyway-core:5.2.4
|
||||
org.glassfish.jaxb:jaxb-runtime:2.3.1
|
||||
org.glassfish.jaxb:txw2:2.3.1
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
|
||||
org.hibernate:hibernate-core:5.4.17.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.17.Final
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
|
||||
org.hibernate:hibernate-core:5.4.23.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.23.Final
|
||||
org.javassist:javassist:3.24.0-GA
|
||||
org.jboss.logging:jboss-logging:3.3.2.Final
|
||||
org.jboss.logging:jboss-logging:3.4.1.Final
|
||||
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
|
||||
org.jboss:jandex:2.1.3.Final
|
||||
org.jetbrains:annotations:19.0.0
|
||||
@@ -233,7 +233,7 @@ org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -130,7 +130,7 @@ com.squareup.okio:okio:1.13.0
|
||||
com.sun.istack:istack-commons-runtime:3.0.7
|
||||
com.sun.xml.fastinfoset:FastInfoset:1.2.15
|
||||
com.thoughtworks.paranamer:paranamer:2.7
|
||||
com.zaxxer:HikariCP:3.2.0
|
||||
com.zaxxer:HikariCP:3.4.5
|
||||
commons-codec:commons-codec:1.13
|
||||
commons-logging:commons-logging:1.2
|
||||
dnsjava:dnsjava:2.1.7
|
||||
@@ -185,7 +185,7 @@ jline:jline:1.0
|
||||
joda-time:joda-time:2.10.5
|
||||
junit:junit:4.12
|
||||
net.arnx:nashorn-promise:0.1.1
|
||||
net.bytebuddy:byte-buddy:1.10.10
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.java.dev.jna:jna-platform:5.5.0
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
@@ -216,7 +216,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
|
||||
org.bouncycastle:bcpkix-jdk15on:1.61
|
||||
org.bouncycastle:bcprov-jdk15on:1.61
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.11.1
|
||||
org.checkerframework:checker-qual:3.5.0
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18
|
||||
@@ -227,11 +227,11 @@ org.flywaydb:flyway-core:5.2.4
|
||||
org.glassfish.jaxb:jaxb-runtime:2.3.1
|
||||
org.glassfish.jaxb:txw2:2.3.1
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
|
||||
org.hibernate:hibernate-core:5.4.17.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.17.Final
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
|
||||
org.hibernate:hibernate-core:5.4.23.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.23.Final
|
||||
org.javassist:javassist:3.24.0-GA
|
||||
org.jboss.logging:jboss-logging:3.3.2.Final
|
||||
org.jboss.logging:jboss-logging:3.4.1.Final
|
||||
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
|
||||
org.jboss:jandex:2.1.3.Final
|
||||
org.jetbrains:annotations:19.0.0
|
||||
@@ -247,7 +247,7 @@ org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -130,7 +130,7 @@ com.squareup.okio:okio:1.13.0
|
||||
com.sun.istack:istack-commons-runtime:3.0.7
|
||||
com.sun.xml.fastinfoset:FastInfoset:1.2.15
|
||||
com.thoughtworks.paranamer:paranamer:2.7
|
||||
com.zaxxer:HikariCP:3.2.0
|
||||
com.zaxxer:HikariCP:3.4.5
|
||||
commons-codec:commons-codec:1.13
|
||||
commons-logging:commons-logging:1.2
|
||||
dnsjava:dnsjava:2.1.7
|
||||
@@ -184,7 +184,7 @@ javax.xml.bind:jaxb-api:2.3.1
|
||||
jline:jline:1.0
|
||||
joda-time:joda-time:2.10.5
|
||||
net.arnx:nashorn-promise:0.1.1
|
||||
net.bytebuddy:byte-buddy:1.10.10
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.java.dev.jna:jna-platform:5.5.0
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
@@ -215,7 +215,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
|
||||
org.bouncycastle:bcpkix-jdk15on:1.61
|
||||
org.bouncycastle:bcprov-jdk15on:1.61
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.11.1
|
||||
org.checkerframework:checker-qual:3.5.0
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18
|
||||
@@ -225,11 +225,11 @@ org.easymock:easymock:3.0
|
||||
org.flywaydb:flyway-core:5.2.4
|
||||
org.glassfish.jaxb:jaxb-runtime:2.3.1
|
||||
org.glassfish.jaxb:txw2:2.3.1
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
|
||||
org.hibernate:hibernate-core:5.4.17.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.17.Final
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
|
||||
org.hibernate:hibernate-core:5.4.23.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.23.Final
|
||||
org.javassist:javassist:3.24.0-GA
|
||||
org.jboss.logging:jboss-logging:3.3.2.Final
|
||||
org.jboss.logging:jboss-logging:3.4.1.Final
|
||||
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
|
||||
org.jboss:jandex:2.1.3.Final
|
||||
org.jetbrains:annotations:19.0.0
|
||||
@@ -245,7 +245,7 @@ org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -125,7 +125,7 @@ com.squareup.okio:okio:1.13.0
|
||||
com.sun.istack:istack-commons-runtime:3.0.7
|
||||
com.sun.xml.fastinfoset:FastInfoset:1.2.15
|
||||
com.thoughtworks.paranamer:paranamer:2.7
|
||||
com.zaxxer:HikariCP:3.2.0
|
||||
com.zaxxer:HikariCP:3.4.5
|
||||
commons-codec:commons-codec:1.13
|
||||
commons-logging:commons-logging:1.2
|
||||
dnsjava:dnsjava:2.1.7
|
||||
@@ -176,7 +176,7 @@ javax.xml.bind:jaxb-api:2.3.1
|
||||
jline:jline:1.0
|
||||
joda-time:joda-time:2.10.5
|
||||
junit:junit:4.12
|
||||
net.bytebuddy:byte-buddy:1.10.10
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.java.dev.jna:jna-platform:5.5.0
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
@@ -205,7 +205,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
|
||||
org.bouncycastle:bcpkix-jdk15on:1.61
|
||||
org.bouncycastle:bcprov-jdk15on:1.61
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.11.1
|
||||
org.checkerframework:checker-qual:3.5.0
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18
|
||||
@@ -216,11 +216,11 @@ org.flywaydb:flyway-core:5.2.4
|
||||
org.glassfish.jaxb:jaxb-runtime:2.3.1
|
||||
org.glassfish.jaxb:txw2:2.3.1
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
|
||||
org.hibernate:hibernate-core:5.4.17.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.17.Final
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
|
||||
org.hibernate:hibernate-core:5.4.23.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.23.Final
|
||||
org.javassist:javassist:3.24.0-GA
|
||||
org.jboss.logging:jboss-logging:3.3.2.Final
|
||||
org.jboss.logging:jboss-logging:3.4.1.Final
|
||||
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
|
||||
org.jboss:jandex:2.1.3.Final
|
||||
org.jetbrains:annotations:19.0.0
|
||||
@@ -236,7 +236,7 @@ org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -124,7 +124,7 @@ com.squareup.okio:okio:1.13.0
|
||||
com.sun.istack:istack-commons-runtime:3.0.7
|
||||
com.sun.xml.fastinfoset:FastInfoset:1.2.15
|
||||
com.thoughtworks.paranamer:paranamer:2.7
|
||||
com.zaxxer:HikariCP:3.2.0
|
||||
com.zaxxer:HikariCP:3.4.5
|
||||
commons-codec:commons-codec:1.13
|
||||
commons-logging:commons-logging:1.2
|
||||
dnsjava:dnsjava:2.1.7
|
||||
@@ -175,7 +175,7 @@ javax.xml.bind:jaxb-api:2.3.1
|
||||
jline:jline:1.0
|
||||
joda-time:joda-time:2.10.5
|
||||
junit:junit:4.12
|
||||
net.bytebuddy:byte-buddy:1.10.10
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.java.dev.jna:jna-platform:5.5.0
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
@@ -215,11 +215,11 @@ org.flywaydb:flyway-core:5.2.4
|
||||
org.glassfish.jaxb:jaxb-runtime:2.3.1
|
||||
org.glassfish.jaxb:txw2:2.3.1
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
|
||||
org.hibernate:hibernate-core:5.4.17.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.17.Final
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
|
||||
org.hibernate:hibernate-core:5.4.23.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.23.Final
|
||||
org.javassist:javassist:3.24.0-GA
|
||||
org.jboss.logging:jboss-logging:3.3.2.Final
|
||||
org.jboss.logging:jboss-logging:3.4.1.Final
|
||||
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
|
||||
org.jboss:jandex:2.1.3.Final
|
||||
org.jetbrains:annotations:19.0.0
|
||||
@@ -235,7 +235,7 @@ org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -129,7 +129,7 @@ com.squareup.okio:okio:1.13.0
|
||||
com.sun.istack:istack-commons-runtime:3.0.7
|
||||
com.sun.xml.fastinfoset:FastInfoset:1.2.15
|
||||
com.thoughtworks.paranamer:paranamer:2.7
|
||||
com.zaxxer:HikariCP:3.2.0
|
||||
com.zaxxer:HikariCP:3.4.5
|
||||
commons-codec:commons-codec:1.13
|
||||
commons-logging:commons-logging:1.2
|
||||
dnsjava:dnsjava:2.1.7
|
||||
@@ -184,7 +184,7 @@ jline:jline:1.0
|
||||
joda-time:joda-time:2.10.5
|
||||
junit:junit:4.12
|
||||
net.arnx:nashorn-promise:0.1.1
|
||||
net.bytebuddy:byte-buddy:1.10.10
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.java.dev.jna:jna-platform:5.5.0
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
@@ -215,7 +215,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
|
||||
org.bouncycastle:bcpkix-jdk15on:1.61
|
||||
org.bouncycastle:bcprov-jdk15on:1.61
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.11.1
|
||||
org.checkerframework:checker-qual:3.5.0
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18
|
||||
@@ -226,11 +226,11 @@ org.flywaydb:flyway-core:5.2.4
|
||||
org.glassfish.jaxb:jaxb-runtime:2.3.1
|
||||
org.glassfish.jaxb:txw2:2.3.1
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
|
||||
org.hibernate:hibernate-core:5.4.17.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.17.Final
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
|
||||
org.hibernate:hibernate-core:5.4.23.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.23.Final
|
||||
org.javassist:javassist:3.24.0-GA
|
||||
org.jboss.logging:jboss-logging:3.3.2.Final
|
||||
org.jboss.logging:jboss-logging:3.4.1.Final
|
||||
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
|
||||
org.jboss:jandex:2.1.3.Final
|
||||
org.jetbrains:annotations:19.0.0
|
||||
@@ -246,7 +246,7 @@ org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -129,7 +129,7 @@ com.squareup.okio:okio:1.13.0
|
||||
com.sun.istack:istack-commons-runtime:3.0.7
|
||||
com.sun.xml.fastinfoset:FastInfoset:1.2.15
|
||||
com.thoughtworks.paranamer:paranamer:2.7
|
||||
com.zaxxer:HikariCP:3.2.0
|
||||
com.zaxxer:HikariCP:3.4.5
|
||||
commons-codec:commons-codec:1.13
|
||||
commons-logging:commons-logging:1.2
|
||||
dnsjava:dnsjava:2.1.7
|
||||
@@ -184,7 +184,7 @@ jline:jline:1.0
|
||||
joda-time:joda-time:2.10.5
|
||||
junit:junit:4.12
|
||||
net.arnx:nashorn-promise:0.1.1
|
||||
net.bytebuddy:byte-buddy:1.10.10
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.java.dev.jna:jna-platform:5.5.0
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
@@ -215,7 +215,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
|
||||
org.bouncycastle:bcpkix-jdk15on:1.61
|
||||
org.bouncycastle:bcprov-jdk15on:1.61
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.11.1
|
||||
org.checkerframework:checker-qual:3.5.0
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18
|
||||
@@ -226,11 +226,11 @@ org.flywaydb:flyway-core:5.2.4
|
||||
org.glassfish.jaxb:jaxb-runtime:2.3.1
|
||||
org.glassfish.jaxb:txw2:2.3.1
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
|
||||
org.hibernate:hibernate-core:5.4.17.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.17.Final
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
|
||||
org.hibernate:hibernate-core:5.4.23.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.23.Final
|
||||
org.javassist:javassist:3.24.0-GA
|
||||
org.jboss.logging:jboss-logging:3.3.2.Final
|
||||
org.jboss.logging:jboss-logging:3.4.1.Final
|
||||
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
|
||||
org.jboss:jandex:2.1.3.Final
|
||||
org.jetbrains:annotations:19.0.0
|
||||
@@ -246,7 +246,7 @@ org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -129,7 +129,7 @@ com.squareup.okio:okio:1.13.0
|
||||
com.sun.istack:istack-commons-runtime:3.0.7
|
||||
com.sun.xml.fastinfoset:FastInfoset:1.2.15
|
||||
com.thoughtworks.paranamer:paranamer:2.7
|
||||
com.zaxxer:HikariCP:3.2.0
|
||||
com.zaxxer:HikariCP:3.4.5
|
||||
commons-codec:commons-codec:1.13
|
||||
commons-logging:commons-logging:1.2
|
||||
dnsjava:dnsjava:2.1.7
|
||||
@@ -184,7 +184,7 @@ jline:jline:1.0
|
||||
joda-time:joda-time:2.10.5
|
||||
junit:junit:4.12
|
||||
net.arnx:nashorn-promise:0.1.1
|
||||
net.bytebuddy:byte-buddy:1.10.10
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.java.dev.jna:jna-platform:5.5.0
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
@@ -215,7 +215,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
|
||||
org.bouncycastle:bcpkix-jdk15on:1.61
|
||||
org.bouncycastle:bcprov-jdk15on:1.61
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.11.1
|
||||
org.checkerframework:checker-qual:3.5.0
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18
|
||||
@@ -226,11 +226,11 @@ org.flywaydb:flyway-core:5.2.4
|
||||
org.glassfish.jaxb:jaxb-runtime:2.3.1
|
||||
org.glassfish.jaxb:txw2:2.3.1
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
|
||||
org.hibernate:hibernate-core:5.4.17.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.17.Final
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
|
||||
org.hibernate:hibernate-core:5.4.23.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.23.Final
|
||||
org.javassist:javassist:3.24.0-GA
|
||||
org.jboss.logging:jboss-logging:3.3.2.Final
|
||||
org.jboss.logging:jboss-logging:3.4.1.Final
|
||||
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
|
||||
org.jboss:jandex:2.1.3.Final
|
||||
org.jetbrains:annotations:19.0.0
|
||||
@@ -246,7 +246,7 @@ org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -130,7 +130,7 @@ com.squareup.okio:okio:1.13.0
|
||||
com.sun.istack:istack-commons-runtime:3.0.7
|
||||
com.sun.xml.fastinfoset:FastInfoset:1.2.15
|
||||
com.thoughtworks.paranamer:paranamer:2.7
|
||||
com.zaxxer:HikariCP:3.2.0
|
||||
com.zaxxer:HikariCP:3.4.5
|
||||
commons-codec:commons-codec:1.13
|
||||
commons-logging:commons-logging:1.2
|
||||
dnsjava:dnsjava:2.1.7
|
||||
@@ -184,7 +184,7 @@ javax.xml.bind:jaxb-api:2.3.1
|
||||
jline:jline:1.0
|
||||
joda-time:joda-time:2.10.5
|
||||
net.arnx:nashorn-promise:0.1.1
|
||||
net.bytebuddy:byte-buddy:1.10.10
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.java.dev.jna:jna-platform:5.5.0
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
@@ -215,7 +215,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
|
||||
org.bouncycastle:bcpkix-jdk15on:1.61
|
||||
org.bouncycastle:bcprov-jdk15on:1.61
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.11.1
|
||||
org.checkerframework:checker-qual:3.5.0
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18
|
||||
@@ -225,11 +225,11 @@ org.easymock:easymock:3.0
|
||||
org.flywaydb:flyway-core:5.2.4
|
||||
org.glassfish.jaxb:jaxb-runtime:2.3.1
|
||||
org.glassfish.jaxb:txw2:2.3.1
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
|
||||
org.hibernate:hibernate-core:5.4.17.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.17.Final
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
|
||||
org.hibernate:hibernate-core:5.4.23.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.23.Final
|
||||
org.javassist:javassist:3.24.0-GA
|
||||
org.jboss.logging:jboss-logging:3.3.2.Final
|
||||
org.jboss.logging:jboss-logging:3.4.1.Final
|
||||
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
|
||||
org.jboss:jandex:2.1.3.Final
|
||||
org.jetbrains:annotations:19.0.0
|
||||
@@ -245,7 +245,7 @@ org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -133,7 +133,7 @@ com.sun.istack:istack-commons-runtime:3.0.7
|
||||
com.sun.xml.fastinfoset:FastInfoset:1.2.15
|
||||
com.thoughtworks.paranamer:paranamer:2.7
|
||||
com.thoughtworks.qdox:qdox:1.12.1
|
||||
com.zaxxer:HikariCP:3.2.0
|
||||
com.zaxxer:HikariCP:3.4.5
|
||||
commons-codec:commons-codec:1.13
|
||||
commons-logging:commons-logging:1.2
|
||||
dnsjava:dnsjava:2.1.7
|
||||
@@ -185,7 +185,7 @@ jline:jline:1.0
|
||||
joda-time:joda-time:2.10.5
|
||||
junit:junit:4.13
|
||||
net.bytebuddy:byte-buddy-agent:1.10.5
|
||||
net.bytebuddy:byte-buddy:1.10.10
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.java.dev.jna:jna-platform:5.5.0
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
@@ -225,7 +225,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
|
||||
org.bouncycastle:bcpkix-jdk15on:1.61
|
||||
org.bouncycastle:bcprov-jdk15on:1.61
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.11.1
|
||||
org.checkerframework:checker-qual:3.5.0
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18
|
||||
@@ -238,11 +238,11 @@ org.glassfish.jaxb:txw2:2.3.1
|
||||
org.hamcrest:hamcrest-all:1.3
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.hamcrest:hamcrest-library:1.3
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
|
||||
org.hibernate:hibernate-core:5.4.17.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.17.Final
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
|
||||
org.hibernate:hibernate-core:5.4.23.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.23.Final
|
||||
org.javassist:javassist:3.24.0-GA
|
||||
org.jboss.logging:jboss-logging:3.3.2.Final
|
||||
org.jboss.logging:jboss-logging:3.4.1.Final
|
||||
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
|
||||
org.jboss:jandex:2.1.3.Final
|
||||
org.jetbrains:annotations:19.0.0
|
||||
@@ -272,7 +272,7 @@ org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -132,7 +132,7 @@ com.sun.istack:istack-commons-runtime:3.0.7
|
||||
com.sun.xml.fastinfoset:FastInfoset:1.2.15
|
||||
com.thoughtworks.paranamer:paranamer:2.7
|
||||
com.thoughtworks.qdox:qdox:1.12.1
|
||||
com.zaxxer:HikariCP:3.2.0
|
||||
com.zaxxer:HikariCP:3.4.5
|
||||
commons-codec:commons-codec:1.13
|
||||
commons-logging:commons-logging:1.2
|
||||
dnsjava:dnsjava:2.1.7
|
||||
@@ -184,7 +184,7 @@ jline:jline:1.0
|
||||
joda-time:joda-time:2.10.5
|
||||
junit:junit:4.13
|
||||
net.bytebuddy:byte-buddy-agent:1.10.5
|
||||
net.bytebuddy:byte-buddy:1.10.10
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.java.dev.jna:jna-platform:5.5.0
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
@@ -237,11 +237,11 @@ org.glassfish.jaxb:txw2:2.3.1
|
||||
org.hamcrest:hamcrest-all:1.3
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.hamcrest:hamcrest-library:1.3
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
|
||||
org.hibernate:hibernate-core:5.4.17.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.17.Final
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
|
||||
org.hibernate:hibernate-core:5.4.23.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.23.Final
|
||||
org.javassist:javassist:3.24.0-GA
|
||||
org.jboss.logging:jboss-logging:3.3.2.Final
|
||||
org.jboss.logging:jboss-logging:3.4.1.Final
|
||||
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
|
||||
org.jboss:jandex:2.1.3.Final
|
||||
org.jetbrains:annotations:19.0.0
|
||||
@@ -271,7 +271,7 @@ org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -138,7 +138,7 @@ com.sun.istack:istack-commons-runtime:3.0.7
|
||||
com.sun.xml.fastinfoset:FastInfoset:1.2.15
|
||||
com.thoughtworks.paranamer:paranamer:2.7
|
||||
com.thoughtworks.qdox:qdox:1.12.1
|
||||
com.zaxxer:HikariCP:3.2.0
|
||||
com.zaxxer:HikariCP:3.4.5
|
||||
commons-codec:commons-codec:1.13
|
||||
commons-logging:commons-logging:1.2
|
||||
dnsjava:dnsjava:2.1.7
|
||||
@@ -195,7 +195,7 @@ joda-time:joda-time:2.10.5
|
||||
junit:junit:4.13
|
||||
net.arnx:nashorn-promise:0.1.1
|
||||
net.bytebuddy:byte-buddy-agent:1.10.5
|
||||
net.bytebuddy:byte-buddy:1.10.10
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.java.dev.jna:jna-platform:5.5.0
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
@@ -236,7 +236,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
|
||||
org.bouncycastle:bcpkix-jdk15on:1.61
|
||||
org.bouncycastle:bcprov-jdk15on:1.61
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.11.1
|
||||
org.checkerframework:checker-qual:3.5.0
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18
|
||||
@@ -250,11 +250,11 @@ org.glassfish.jaxb:txw2:2.3.1
|
||||
org.hamcrest:hamcrest-all:1.3
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.hamcrest:hamcrest-library:1.3
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
|
||||
org.hibernate:hibernate-core:5.4.17.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.17.Final
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
|
||||
org.hibernate:hibernate-core:5.4.23.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.23.Final
|
||||
org.javassist:javassist:3.24.0-GA
|
||||
org.jboss.logging:jboss-logging:3.3.2.Final
|
||||
org.jboss.logging:jboss-logging:3.4.1.Final
|
||||
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
|
||||
org.jboss:jandex:2.1.3.Final
|
||||
org.jetbrains:annotations:19.0.0
|
||||
@@ -284,7 +284,7 @@ org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -138,7 +138,7 @@ com.sun.istack:istack-commons-runtime:3.0.7
|
||||
com.sun.xml.fastinfoset:FastInfoset:1.2.15
|
||||
com.thoughtworks.paranamer:paranamer:2.7
|
||||
com.thoughtworks.qdox:qdox:1.12.1
|
||||
com.zaxxer:HikariCP:3.2.0
|
||||
com.zaxxer:HikariCP:3.4.5
|
||||
commons-codec:commons-codec:1.13
|
||||
commons-logging:commons-logging:1.2
|
||||
dnsjava:dnsjava:2.1.7
|
||||
@@ -195,7 +195,7 @@ joda-time:joda-time:2.10.5
|
||||
junit:junit:4.13
|
||||
net.arnx:nashorn-promise:0.1.1
|
||||
net.bytebuddy:byte-buddy-agent:1.10.5
|
||||
net.bytebuddy:byte-buddy:1.10.10
|
||||
net.bytebuddy:byte-buddy:1.10.17
|
||||
net.java.dev.jna:jna-platform:5.5.0
|
||||
net.java.dev.jna:jna:5.5.0
|
||||
org.apache.avro:avro:1.8.2
|
||||
@@ -236,7 +236,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
|
||||
org.bouncycastle:bcpkix-jdk15on:1.61
|
||||
org.bouncycastle:bcprov-jdk15on:1.61
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.11.1
|
||||
org.checkerframework:checker-qual:3.5.0
|
||||
org.codehaus.jackson:jackson-core-asl:1.9.13
|
||||
org.codehaus.jackson:jackson-mapper-asl:1.9.13
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18
|
||||
@@ -250,11 +250,11 @@ org.glassfish.jaxb:txw2:2.3.1
|
||||
org.hamcrest:hamcrest-all:1.3
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.hamcrest:hamcrest-library:1.3
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
|
||||
org.hibernate:hibernate-core:5.4.17.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.17.Final
|
||||
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
|
||||
org.hibernate:hibernate-core:5.4.23.Final
|
||||
org.hibernate:hibernate-hikaricp:5.4.23.Final
|
||||
org.javassist:javassist:3.24.0-GA
|
||||
org.jboss.logging:jboss-logging:3.3.2.Final
|
||||
org.jboss.logging:jboss-logging:3.4.1.Final
|
||||
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
|
||||
org.jboss:jandex:2.1.3.Final
|
||||
org.jetbrains:annotations:19.0.0
|
||||
@@ -284,7 +284,7 @@ org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.config;
|
||||
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.util.CertificateChecker;
|
||||
import google.registry.util.Clock;
|
||||
import javax.inject.Singleton;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Dagger module that provides the {@link CertificateChecker} used in the application. */
|
||||
// TODO(sarahbot@): Move this module to a better location. Possibly flows/. If we decide to move
|
||||
// CertificateChecker.java to core/ delete this file and inject the CertificateChecker constructor
|
||||
// instead.
|
||||
@Module
|
||||
public abstract class CertificateCheckerModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static CertificateChecker provideCertificateChecker(
|
||||
@Config("maxValidityDaysSchedule") ImmutableSortedMap<DateTime, Integer> validityDaysMap,
|
||||
@Config("expirationWarningDays") int daysToExpiration,
|
||||
@Config("minimumRsaKeyLength") int minimumRsaKeyLength,
|
||||
Clock clock) {
|
||||
return new CertificateChecker(validityDaysMap, daysToExpiration, minimumRsaKeyLength, clock);
|
||||
}
|
||||
|
||||
private CertificateCheckerModule() {}
|
||||
}
|
||||
+10
-4
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.util;
|
||||
package google.registry.flows.certs;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
@@ -20,6 +20,9 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.CertificateException;
|
||||
@@ -28,6 +31,7 @@ import java.security.cert.X509Certificate;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.Date;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Days;
|
||||
|
||||
@@ -58,10 +62,12 @@ public class CertificateChecker {
|
||||
* );
|
||||
* </pre>
|
||||
*/
|
||||
@Inject
|
||||
public CertificateChecker(
|
||||
ImmutableSortedMap<DateTime, Integer> maxValidityLengthSchedule,
|
||||
int daysToExpiration,
|
||||
int minimumRsaKeyLength,
|
||||
@Config("maxValidityDaysSchedule")
|
||||
ImmutableSortedMap<DateTime, Integer> maxValidityLengthSchedule,
|
||||
@Config("expirationWarningDays") int daysToExpiration,
|
||||
@Config("minimumRsaKeyLength") int minimumRsaKeyLength,
|
||||
Clock clock) {
|
||||
checkArgument(
|
||||
maxValidityLengthSchedule.containsKey(START_OF_TIME),
|
||||
@@ -61,7 +61,17 @@ public abstract class ImmutableObject implements Cloneable {
|
||||
private boolean equalsImmutableObject(ImmutableObject other) {
|
||||
return getClass().equals(other.getClass())
|
||||
&& hashCode() == other.hashCode()
|
||||
&& ModelUtils.getFieldValues(this).equals(ModelUtils.getFieldValues(other));
|
||||
&& getSignificantFields().equals(other.getSignificantFields());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map of significant fields (fields that we care about for purposes of comparison and
|
||||
* display).
|
||||
*
|
||||
* <p>Isolated into a method so that derived classes can override it.
|
||||
*/
|
||||
protected Map<Field, Object> getSignificantFields() {
|
||||
return ModelUtils.getFieldValues(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,7 +82,7 @@ public abstract class ImmutableObject implements Cloneable {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (hashCode == null) {
|
||||
hashCode = Arrays.hashCode(ModelUtils.getFieldValues(this).values().toArray());
|
||||
hashCode = Arrays.hashCode(getSignificantFields().values().toArray());
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
@@ -111,7 +121,7 @@ public abstract class ImmutableObject implements Cloneable {
|
||||
@Override
|
||||
public String toString() {
|
||||
NavigableMap<String, Object> sortedFields = new TreeMap<>();
|
||||
for (Entry<Field, Object> entry : ModelUtils.getFieldValues(this).entrySet()) {
|
||||
for (Entry<Field, Object> entry : getSignificantFields().entrySet()) {
|
||||
sortedFields.put(entry.getKey().getName(), entry.getValue());
|
||||
}
|
||||
return toStringHelper(sortedFields);
|
||||
@@ -121,7 +131,7 @@ public abstract class ImmutableObject implements Cloneable {
|
||||
public String toHydratedString() {
|
||||
// We can't use ImmutableSortedMap because we need to allow null values.
|
||||
NavigableMap<String, Object> sortedFields = new TreeMap<>();
|
||||
for (Entry<Field, Object> entry : ModelUtils.getFieldValues(this).entrySet()) {
|
||||
for (Entry<Field, Object> entry : getSignificantFields().entrySet()) {
|
||||
Field field = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
sortedFields.put(
|
||||
@@ -161,7 +171,7 @@ public abstract class ImmutableObject implements Cloneable {
|
||||
// LinkedHashMap to preserve field ordering and because ImmutableMap forbids null
|
||||
// values.
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
for (Entry<Field, Object> entry : ModelUtils.getFieldValues(o).entrySet()) {
|
||||
for (Entry<Field, Object> entry : ((ImmutableObject) o).getSignificantFields().entrySet()) {
|
||||
Field field = entry.getKey();
|
||||
if (!field.isAnnotationPresent(IgnoredInDiffableMap.class)) {
|
||||
result.put(field.getName(), toMapRecursive(entry.getValue()));
|
||||
|
||||
@@ -194,7 +194,7 @@ public class ModelUtils {
|
||||
* returned map in its implementation of {@link ImmutableObject#toString} and {@link
|
||||
* ImmutableObject#equals}, which work by comparing and printing these maps.
|
||||
*/
|
||||
static Map<Field, Object> getFieldValues(Object instance) {
|
||||
public static Map<Field, Object> getFieldValues(Object instance) {
|
||||
// Don't make this ImmutableMap because field values can be null.
|
||||
Map<Field, Object> values = new LinkedHashMap<>();
|
||||
for (Field field : getAllFields(instance.getClass()).values()) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
@@ -49,6 +50,8 @@ import google.registry.model.transfer.TransferData.TransferServerApproveEntity;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithLongVKey;
|
||||
import google.registry.schema.replay.DatastoreAndSqlEntity;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -685,7 +688,7 @@ public abstract class BillingEvent extends ImmutableObject
|
||||
@ReportedOn
|
||||
@Entity
|
||||
@WithLongVKey
|
||||
public static class Modification extends BillingEvent implements DatastoreAndSqlEntity {
|
||||
public static class Modification extends BillingEvent implements DatastoreEntity {
|
||||
|
||||
/** The change in cost that should be applied to the original billing event. */
|
||||
Money cost;
|
||||
@@ -747,6 +750,11 @@ public abstract class BillingEvent extends ImmutableObject
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // not persisted in SQL
|
||||
}
|
||||
|
||||
/** A builder for {@link Modification} since it is immutable. */
|
||||
public static class Builder extends BillingEvent.Builder<Modification, Builder> {
|
||||
|
||||
|
||||
@@ -60,7 +60,9 @@ public class ContactHistory extends HistoryEntry implements SqlEntity {
|
||||
@Id
|
||||
@Access(AccessType.PROPERTY)
|
||||
public String getContactRepoId() {
|
||||
return parent.getName();
|
||||
// We need to handle null case here because Hibernate sometimes accesses this method before
|
||||
// parent gets initialized
|
||||
return parent == null ? null : parent.getName();
|
||||
}
|
||||
|
||||
/** This method is private because it is only used by Hibernate. */
|
||||
|
||||
@@ -303,14 +303,13 @@ public class DomainContent extends EppResource
|
||||
allContacts.stream().map(DesignatedContact::reconstitute).collect(toImmutableSet());
|
||||
setContactFields(allContacts, true);
|
||||
|
||||
// We have to return the cloned object here because the original object's
|
||||
// hashcode is not correct due to the change to its domainRepoId. The cloned
|
||||
// object will have a null hashcode so that it can get a recalculated hashcode
|
||||
// when its hashCode() is invoked.
|
||||
// We have to return the cloned object here because the original object's hashcode is not
|
||||
// correct due to the change to its domainRepoId and history ids. The cloned object will have a
|
||||
// null hashcode so that it can get a recalculated hashcode when its hashCode() is invoked.
|
||||
// TODO(b/162739503): Remove this after fully migrating to Cloud SQL.
|
||||
gracePeriods =
|
||||
nullToEmptyImmutableCopy(gracePeriods).stream()
|
||||
.map(gracePeriod -> gracePeriod.cloneWithDomainRepoId(getRepoId()))
|
||||
.map(gracePeriod -> gracePeriod.cloneAfterOfyLoad(getRepoId()))
|
||||
.collect(toImmutableSet());
|
||||
|
||||
// Restore history record ids.
|
||||
|
||||
@@ -14,14 +14,17 @@
|
||||
|
||||
package google.registry.model.domain;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.EntitySubclass;
|
||||
import com.googlecode.objectify.annotation.Ignore;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||
import google.registry.model.domain.secdns.DomainDsDataHistory;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
@@ -40,10 +43,12 @@ import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinColumns;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.PostLoad;
|
||||
@@ -75,7 +80,9 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
|
||||
@Id
|
||||
@Access(AccessType.PROPERTY)
|
||||
public String getDomainRepoId() {
|
||||
return parent.getName();
|
||||
// We need to handle null case here because Hibernate sometimes accesses this method before
|
||||
// parent gets initialized
|
||||
return parent == null ? null : parent.getName();
|
||||
}
|
||||
|
||||
/** This method is private because it is only used by Hibernate. */
|
||||
@@ -93,6 +100,24 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
|
||||
@Column(name = "host_repo_id")
|
||||
Set<VKey<HostResource>> nsHosts;
|
||||
|
||||
@OneToMany(
|
||||
cascade = {CascadeType.ALL},
|
||||
fetch = FetchType.EAGER,
|
||||
orphanRemoval = true)
|
||||
@JoinColumns({
|
||||
@JoinColumn(
|
||||
name = "domainHistoryRevisionId",
|
||||
referencedColumnName = "historyRevisionId",
|
||||
insertable = false,
|
||||
updatable = false),
|
||||
@JoinColumn(
|
||||
name = "domainRepoId",
|
||||
referencedColumnName = "domainRepoId",
|
||||
insertable = false,
|
||||
updatable = false)
|
||||
})
|
||||
Set<DomainDsDataHistory> dsDataHistories;
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
@Access(AccessType.PROPERTY)
|
||||
@@ -127,14 +152,24 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
|
||||
*
|
||||
* <p>This will be empty for any DomainHistory/HistoryEntry generated before this field was added,
|
||||
* mid-2017, as well as any action that does not generate billable events (e.g. updates).
|
||||
*
|
||||
* <p>This method is dedicated for Hibernate, external caller should use {@link
|
||||
* #getDomainTransactionRecords()}.
|
||||
*/
|
||||
@Access(AccessType.PROPERTY)
|
||||
@OneToMany(cascade = {CascadeType.ALL})
|
||||
@JoinColumn(name = "historyRevisionId", referencedColumnName = "historyRevisionId")
|
||||
@JoinColumn(name = "domainRepoId", referencedColumnName = "domainRepoId")
|
||||
@Override
|
||||
public Set<DomainTransactionRecord> getDomainTransactionRecords() {
|
||||
return super.getDomainTransactionRecords();
|
||||
@SuppressWarnings("unused")
|
||||
private Set<DomainTransactionRecord> getInternalDomainTransactionRecords() {
|
||||
return domainTransactionRecords;
|
||||
}
|
||||
|
||||
/** Sets the domain transaction records. This method is dedicated for Hibernate. */
|
||||
@SuppressWarnings("unused")
|
||||
private void setInternalDomainTransactionRecords(
|
||||
Set<DomainTransactionRecord> domainTransactionRecords) {
|
||||
this.domainTransactionRecords = domainTransactionRecords;
|
||||
}
|
||||
|
||||
@Id
|
||||
@@ -150,6 +185,11 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
|
||||
return nsHosts;
|
||||
}
|
||||
|
||||
/** Returns the collection of {@link DomainDsDataHistory} instances. */
|
||||
public ImmutableSet<DomainDsDataHistory> getDsDataHistories() {
|
||||
return nullToEmptyImmutableCopy(dsDataHistories);
|
||||
}
|
||||
|
||||
/**
|
||||
* The values of all the fields on the {@link DomainContent} object after the action represented
|
||||
* by this history object was executed.
|
||||
@@ -266,9 +306,6 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
|
||||
|
||||
public Builder setDomainContent(DomainContent domainContent) {
|
||||
getInstance().domainContent = domainContent;
|
||||
if (domainContent != null) {
|
||||
getInstance().nsHosts = nullToEmptyImmutableCopy(domainContent.nsHosts);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -276,5 +313,22 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
|
||||
getInstance().parent = Key.create(DomainBase.class, domainRepoId);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainHistory build() {
|
||||
DomainHistory instance = super.build();
|
||||
// TODO(b/171990736): Assert instance.domainContent is not null after database migration.
|
||||
// Note that we cannot assert that instance.domainContent is not null here because this
|
||||
// builder is also used to convert legacy HistoryEntry objects to DomainHistory, when
|
||||
// domainContent is not available.
|
||||
if (instance.domainContent != null) {
|
||||
instance.nsHosts = nullToEmptyImmutableCopy(instance.domainContent.nsHosts);
|
||||
instance.dsDataHistories =
|
||||
nullToEmptyImmutableCopy(instance.domainContent.getDsData()).stream()
|
||||
.map(dsData -> DomainDsDataHistory.createFrom(instance.id, dsData))
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.googlecode.objectify.annotation.Embed;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.ofy.ObjectifyService;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.schema.replay.DatastoreAndSqlEntity;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -53,12 +54,15 @@ public class GracePeriod extends GracePeriodBase implements DatastoreAndSqlEntit
|
||||
(billingEventRecurring != null) == GracePeriodStatus.AUTO_RENEW.equals(type),
|
||||
"Recurring billing events must be present on (and only on) autorenew grace periods");
|
||||
GracePeriod instance = new GracePeriod();
|
||||
instance.id = ObjectifyService.allocateId();
|
||||
instance.type = checkArgumentNotNull(type);
|
||||
instance.domainRepoId = checkArgumentNotNull(domainRepoId);
|
||||
instance.expirationTime = checkArgumentNotNull(expirationTime);
|
||||
instance.clientId = checkArgumentNotNull(clientId);
|
||||
instance.billingEventOneTime = billingEventOneTime;
|
||||
instance.billingEventOneTimeHistoryId = DomainBase.getHistoryId(billingEventOneTime);
|
||||
instance.billingEventRecurring = billingEventRecurring;
|
||||
instance.billingEventRecurringHistoryId = DomainBase.getHistoryId(billingEventRecurring);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -108,14 +112,28 @@ public class GracePeriod extends GracePeriodBase implements DatastoreAndSqlEntit
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a clone of this {@link GracePeriod} with {@link #domainRepoId} set to the given value.
|
||||
* Returns a clone of this {@link GracePeriod} with {@link #domainRepoId} set to the given value
|
||||
* and reconstructed history ids.
|
||||
*
|
||||
* <p>TODO(b/162739503): Remove this function after fully migrating to Cloud SQL.
|
||||
*/
|
||||
public GracePeriod cloneWithDomainRepoId(String domainRepoId) {
|
||||
public GracePeriod cloneAfterOfyLoad(String domainRepoId) {
|
||||
GracePeriod clone = clone(this);
|
||||
clone.id = ObjectifyService.allocateId();
|
||||
clone.domainRepoId = checkArgumentNotNull(domainRepoId);
|
||||
clone.restoreHistoryIds();
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a clone of this {@link GracePeriod} with {@link #billingEventRecurring} set to the
|
||||
* given value.
|
||||
*
|
||||
* <p>TODO(b/162231099): Remove this function after duplicate id issue is solved.
|
||||
*/
|
||||
public GracePeriod cloneWithRecurringBillingEvent(VKey<BillingEvent.Recurring> recurring) {
|
||||
GracePeriod clone = clone(this);
|
||||
clone.billingEventRecurring = recurring;
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,18 +14,21 @@
|
||||
|
||||
package google.registry.model.domain;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Embed;
|
||||
import com.googlecode.objectify.annotation.Ignore;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.ModelUtils;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.OneTime;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -36,7 +39,6 @@ public class GracePeriodBase extends ImmutableObject {
|
||||
|
||||
/** Unique id required for hibernate representation. */
|
||||
@javax.persistence.Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Ignore
|
||||
Long id;
|
||||
|
||||
@@ -67,6 +69,10 @@ public class GracePeriodBase extends ImmutableObject {
|
||||
@Column(name = "billing_event_id")
|
||||
VKey<OneTime> billingEventOneTime = null;
|
||||
|
||||
@Ignore
|
||||
@Column(name = "billing_event_history_id")
|
||||
Long billingEventOneTimeHistoryId;
|
||||
|
||||
/**
|
||||
* The recurring billing event corresponding to the action that triggered this grace period, if
|
||||
* applicable - i.e. if the action was an autorenew - or null in all other cases.
|
||||
@@ -75,6 +81,14 @@ public class GracePeriodBase extends ImmutableObject {
|
||||
@Column(name = "billing_recurrence_id")
|
||||
VKey<BillingEvent.Recurring> billingEventRecurring = null;
|
||||
|
||||
@Ignore
|
||||
@Column(name = "billing_recurrence_history_id")
|
||||
Long billingEventRecurringHistoryId;
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public GracePeriodStatus getType() {
|
||||
return type;
|
||||
}
|
||||
@@ -101,6 +115,7 @@ public class GracePeriodBase extends ImmutableObject {
|
||||
* period is not AUTO_RENEW.
|
||||
*/
|
||||
public VKey<BillingEvent.OneTime> getOneTimeBillingEvent() {
|
||||
restoreOfyKeys();
|
||||
return billingEventOneTime;
|
||||
}
|
||||
|
||||
@@ -109,6 +124,63 @@ public class GracePeriodBase extends ImmutableObject {
|
||||
* period is AUTO_RENEW.
|
||||
*/
|
||||
public VKey<BillingEvent.Recurring> getRecurringBillingEvent() {
|
||||
restoreOfyKeys();
|
||||
return billingEventRecurring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores history ids for composite VKeys after a load from datastore.
|
||||
*
|
||||
* <p>For use by DomainContent.load() ONLY.
|
||||
*/
|
||||
protected void restoreHistoryIds() {
|
||||
billingEventOneTimeHistoryId = DomainBase.getHistoryId(billingEventOneTime);
|
||||
billingEventRecurringHistoryId = DomainBase.getHistoryId(billingEventRecurring);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override {@link ImmutableObject#getSignificantFields()} to exclude "id", which breaks equality
|
||||
* testing in the unit tests.
|
||||
*/
|
||||
@Override
|
||||
protected Map<Field, Object> getSignificantFields() {
|
||||
restoreOfyKeys();
|
||||
// Can't use streams or ImmutableMap because we can have null values.
|
||||
Map<Field, Object> result = new LinkedHashMap();
|
||||
for (Map.Entry<Field, Object> entry : ModelUtils.getFieldValues(this).entrySet()) {
|
||||
if (!entry.getKey().getName().equals("id")) {
|
||||
result.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores Ofy keys in the billing events.
|
||||
*
|
||||
* <p>This must be called by all methods that access the one time or recurring billing event keys.
|
||||
* When the billing event keys are loaded from SQL, they are loaded as asymmetric keys because the
|
||||
* database columns that we load them from do not contain all of the information necessary to
|
||||
* reconsitute the Ofy side of the key. In other cases, we restore the Ofy key during the
|
||||
* hibernate {@link javax.persistence.PostLoad} method from the other fields of the object, but we
|
||||
* have been unable to make this work with hibernate's internal persistence model in this case
|
||||
* because the {@link GracePeriod}'s hash code is evaluated prior to these calls, and would be
|
||||
* invalidated by changing the fields.
|
||||
*/
|
||||
private final synchronized void restoreOfyKeys() {
|
||||
if (billingEventOneTime != null && !billingEventOneTime.maybeGetOfyKey().isPresent()) {
|
||||
billingEventOneTime =
|
||||
DomainBase.restoreOfyFrom(
|
||||
Key.create(DomainBase.class, domainRepoId),
|
||||
billingEventOneTime,
|
||||
billingEventOneTimeHistoryId);
|
||||
}
|
||||
if (billingEventRecurring != null && !billingEventRecurring.maybeGetOfyKey().isPresent()) {
|
||||
billingEventRecurring =
|
||||
DomainBase.restoreOfyFrom(
|
||||
Key.create(DomainBase.class, domainRepoId),
|
||||
billingEventRecurring,
|
||||
billingEventRecurringHistoryId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,89 +17,68 @@ package google.registry.model.domain.secdns;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.googlecode.objectify.annotation.Embed;
|
||||
import com.googlecode.objectify.annotation.Ignore;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData.DelegationSignerDataId;
|
||||
import google.registry.schema.replay.DatastoreAndSqlEntity;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData.DomainDsDataId;
|
||||
import java.io.Serializable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.Table;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
|
||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
|
||||
/**
|
||||
* Holds the data necessary to construct a single Delegation Signer (DS) record for a domain.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5910">RFC 5910</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4034">RFC 4034</a>
|
||||
* <p>TODO(shicong): Rename this class to DomainDsData.
|
||||
*/
|
||||
@Embed
|
||||
@XmlType(name = "dsData")
|
||||
@Entity
|
||||
@IdClass(DomainDsDataId.class)
|
||||
@Table(indexes = @Index(columnList = "domainRepoId"))
|
||||
@IdClass(DelegationSignerDataId.class)
|
||||
public class DelegationSignerData extends ImmutableObject implements DatastoreAndSqlEntity {
|
||||
public class DelegationSignerData extends DomainDsDataBase {
|
||||
|
||||
private DelegationSignerData() {}
|
||||
|
||||
@Ignore @XmlTransient @javax.persistence.Id String domainRepoId;
|
||||
|
||||
/** The identifier for this particular key in the domain. */
|
||||
@javax.persistence.Id
|
||||
@Column(nullable = false)
|
||||
int keyTag;
|
||||
|
||||
/**
|
||||
* The algorithm used by this key.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4034#appendix-A.1">RFC 4034 Appendix A.1</a>
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
@XmlElement(name = "alg")
|
||||
int algorithm;
|
||||
|
||||
/**
|
||||
* The algorithm used to generate the digest.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4034#appendix-A.2">RFC 4034 Appendix A.2</a>
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
int digestType;
|
||||
|
||||
/**
|
||||
* The hexBinary digest of the public key.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4034#section-5.1.4">RFC 4034 Section 5.1.4</a>
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
@XmlJavaTypeAdapter(HexBinaryAdapter.class)
|
||||
byte[] digest;
|
||||
@Override
|
||||
@Id
|
||||
@Access(AccessType.PROPERTY)
|
||||
public String getDomainRepoId() {
|
||||
return super.getDomainRepoId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Id
|
||||
@Access(AccessType.PROPERTY)
|
||||
public int getKeyTag() {
|
||||
return keyTag;
|
||||
return super.getKeyTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Id
|
||||
@Access(AccessType.PROPERTY)
|
||||
public int getAlgorithm() {
|
||||
return algorithm;
|
||||
return super.getAlgorithm();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Id
|
||||
@Access(AccessType.PROPERTY)
|
||||
public int getDigestType() {
|
||||
return digestType;
|
||||
return super.getDigestType();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Id
|
||||
@Access(AccessType.PROPERTY)
|
||||
public byte[] getDigest() {
|
||||
return digest;
|
||||
}
|
||||
|
||||
public String getDigestAsString() {
|
||||
return digest == null ? "" : DatatypeConverter.printHexBinary(digest);
|
||||
return super.getDigest();
|
||||
}
|
||||
|
||||
public DelegationSignerData cloneWithDomainRepoId(String domainRepoId) {
|
||||
@@ -135,30 +114,135 @@ public class DelegationSignerData extends ImmutableObject implements DatastoreAn
|
||||
return create(keyTag, algorithm, digestType, DatatypeConverter.parseHexBinary(digestAsHex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the presentation format of this DS record.
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc4034#section-5.3">RFC 4034 Section 5.3</a>
|
||||
*/
|
||||
public String toRrData() {
|
||||
return String.format(
|
||||
"%d %d %d %s",
|
||||
this.keyTag, this.algorithm, this.digestType, DatatypeConverter.printHexBinary(digest));
|
||||
}
|
||||
/** Class to represent the composite primary key of {@link DelegationSignerData} entity. */
|
||||
static class DomainDsDataId extends ImmutableObject implements Serializable {
|
||||
|
||||
static class DelegationSignerDataId extends ImmutableObject implements Serializable {
|
||||
String domainRepoId;
|
||||
|
||||
int keyTag;
|
||||
|
||||
private DelegationSignerDataId() {}
|
||||
int algorithm;
|
||||
|
||||
private DelegationSignerDataId(String domainRepoId, int keyTag) {
|
||||
int digestType;
|
||||
|
||||
byte[] digest;
|
||||
|
||||
/** Hibernate requires this default constructor. */
|
||||
private DomainDsDataId() {}
|
||||
|
||||
/** Constructs a {link DomainDsDataId} instance. */
|
||||
DomainDsDataId(String domainRepoId, int keyTag, int algorithm, int digestType, byte[] digest) {
|
||||
this.domainRepoId = domainRepoId;
|
||||
this.keyTag = keyTag;
|
||||
this.algorithm = algorithm;
|
||||
this.digestType = digestType;
|
||||
this.digest = digest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the domain repository ID.
|
||||
*
|
||||
* <p>This method is private because it is only used by Hibernate.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private String getDomainRepoId() {
|
||||
return domainRepoId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key tag.
|
||||
*
|
||||
* <p>This method is private because it is only used by Hibernate.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private int getKeyTag() {
|
||||
return keyTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the algorithm.
|
||||
*
|
||||
* <p>This method is private because it is only used by Hibernate.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private int getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the digest type.
|
||||
*
|
||||
* <p>This method is private because it is only used by Hibernate.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private int getDigestType() {
|
||||
return digestType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the digest.
|
||||
*
|
||||
* <p>This method is private because it is only used by Hibernate.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private byte[] getDigest() {
|
||||
return digest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the domain repository ID.
|
||||
*
|
||||
* <p>This method is private because it is only used by Hibernate.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private void setDomainRepoId(String domainRepoId) {
|
||||
this.domainRepoId = domainRepoId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the key tag.
|
||||
*
|
||||
* <p>This method is private because it is only used by Hibernate.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private void setKeyTag(int keyTag) {
|
||||
this.keyTag = keyTag;
|
||||
}
|
||||
|
||||
public static DelegationSignerDataId create(String domainRepoId, int keyTag) {
|
||||
return new DelegationSignerDataId(checkArgumentNotNull(domainRepoId), keyTag);
|
||||
/**
|
||||
* Sets the algorithm.
|
||||
*
|
||||
* <p>This method is private because it is only used by Hibernate.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private void setAlgorithm(int algorithm) {
|
||||
this.algorithm = algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the digest type.
|
||||
*
|
||||
* <p>This method is private because it is only used by Hibernate.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private void setDigestType(int digestType) {
|
||||
this.digestType = digestType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the digest.
|
||||
*
|
||||
* <p>This method is private because it is only used by Hibernate.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private void setDigest(byte[] digest) {
|
||||
this.digest = digest;
|
||||
}
|
||||
|
||||
public static DomainDsDataId create(
|
||||
String domainRepoId, int keyTag, int algorithm, int digestType, byte[] digest) {
|
||||
return new DomainDsDataId(
|
||||
domainRepoId, keyTag, algorithm, digestType, checkArgumentNotNull(digest));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.domain.secdns;
|
||||
|
||||
import com.googlecode.objectify.annotation.Embed;
|
||||
import com.googlecode.objectify.annotation.Ignore;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.Transient;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
|
||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
|
||||
/** Base class for {@link DelegationSignerData} and {@link DomainDsDataHistory}. */
|
||||
@Embed
|
||||
@MappedSuperclass
|
||||
@Access(AccessType.FIELD)
|
||||
public abstract class DomainDsDataBase extends ImmutableObject {
|
||||
|
||||
@Ignore @XmlTransient @Transient String domainRepoId;
|
||||
|
||||
/** The identifier for this particular key in the domain. */
|
||||
@Transient int keyTag;
|
||||
|
||||
/**
|
||||
* The algorithm used by this key.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4034#appendix-A.1">RFC 4034 Appendix A.1</a>
|
||||
*/
|
||||
@Transient
|
||||
@XmlElement(name = "alg")
|
||||
int algorithm;
|
||||
|
||||
/**
|
||||
* The algorithm used to generate the digest.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4034#appendix-A.2">RFC 4034 Appendix A.2</a>
|
||||
*/
|
||||
@Transient int digestType;
|
||||
|
||||
/**
|
||||
* The hexBinary digest of the public key.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4034#section-5.1.4">RFC 4034 Section 5.1.4</a>
|
||||
*/
|
||||
@Transient
|
||||
@XmlJavaTypeAdapter(HexBinaryAdapter.class)
|
||||
byte[] digest;
|
||||
|
||||
public String getDomainRepoId() {
|
||||
return domainRepoId;
|
||||
}
|
||||
|
||||
public int getKeyTag() {
|
||||
return keyTag;
|
||||
}
|
||||
|
||||
public int getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
public int getDigestType() {
|
||||
return digestType;
|
||||
}
|
||||
|
||||
public byte[] getDigest() {
|
||||
return digest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the domain repository ID.
|
||||
*
|
||||
* <p>This method is private because it is only used by Hibernate.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private void setDomainRepoId(String domainRepoId) {
|
||||
this.domainRepoId = domainRepoId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the key tag.
|
||||
*
|
||||
* <p>This method is private because it is only used by Hibernate.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private void setKeyTag(int keyTag) {
|
||||
this.keyTag = keyTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the algorithm.
|
||||
*
|
||||
* <p>This method is private because it is only used by Hibernate.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private void setAlgorithm(int algorithm) {
|
||||
this.algorithm = algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the digest type.
|
||||
*
|
||||
* <p>This method is private because it is only used by Hibernate.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private void setDigestType(int digestType) {
|
||||
this.digestType = digestType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the digest.
|
||||
*
|
||||
* <p>This method is private because it is only used by Hibernate.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private void setDigest(byte[] digest) {
|
||||
this.digest = digest;
|
||||
}
|
||||
|
||||
public String getDigestAsString() {
|
||||
return digest == null ? "" : DatatypeConverter.printHexBinary(digest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the presentation format of this DS record.
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc4034#section-5.3">RFC 4034 Section 5.3</a>
|
||||
*/
|
||||
public String toRrData() {
|
||||
return String.format(
|
||||
"%d %d %d %s",
|
||||
this.keyTag, this.algorithm, this.digestType, DatatypeConverter.printHexBinary(digest));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.domain.secdns;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.ofy.ObjectifyService;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/** Entity class to represent a historic {@link DelegationSignerData}. */
|
||||
@Entity
|
||||
public class DomainDsDataHistory extends DomainDsDataBase implements SqlEntity {
|
||||
|
||||
@Id Long dsDataHistoryRevisionId;
|
||||
|
||||
/** ID of the {@link DomainHistory} entity that this entity is associated with. */
|
||||
@Column(nullable = false)
|
||||
Long domainHistoryRevisionId;
|
||||
|
||||
private DomainDsDataHistory() {}
|
||||
|
||||
/**
|
||||
* Creates a {@link DomainDsDataHistory} instance from given {@link #domainHistoryRevisionId} and
|
||||
* {@link DelegationSignerData} instance.
|
||||
*/
|
||||
public static DomainDsDataHistory createFrom(
|
||||
long domainHistoryRevisionId, DelegationSignerData dsData) {
|
||||
DomainDsDataHistory instance = new DomainDsDataHistory();
|
||||
instance.domainHistoryRevisionId = domainHistoryRevisionId;
|
||||
instance.domainRepoId = dsData.domainRepoId;
|
||||
instance.keyTag = dsData.getKeyTag();
|
||||
instance.algorithm = dsData.getAlgorithm();
|
||||
instance.digestType = dsData.getDigestType();
|
||||
instance.digest = dsData.getDigest();
|
||||
instance.dsDataHistoryRevisionId = ObjectifyService.allocateId();
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Access(AccessType.PROPERTY)
|
||||
public String getDomainRepoId() {
|
||||
return super.getDomainRepoId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Access(AccessType.PROPERTY)
|
||||
public int getKeyTag() {
|
||||
return super.getKeyTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Access(AccessType.PROPERTY)
|
||||
public int getAlgorithm() {
|
||||
return super.getAlgorithm();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Access(AccessType.PROPERTY)
|
||||
public int getDigestType() {
|
||||
return super.getDigestType();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Access(AccessType.PROPERTY)
|
||||
@Column(nullable = false)
|
||||
public byte[] getDigest() {
|
||||
return super.getDigest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
|
||||
return ImmutableList.of(); // not persisted in Datastore
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,9 @@ public class HostHistory extends HistoryEntry implements SqlEntity {
|
||||
@Id
|
||||
@Access(AccessType.PROPERTY)
|
||||
public String getHostRepoId() {
|
||||
return parent.getName();
|
||||
// We need to handle null case here because Hibernate sometimes accesses this method before
|
||||
// parent gets initialized
|
||||
return parent == null ? null : parent.getName();
|
||||
}
|
||||
|
||||
/** This method is private because it is only used by Hibernate. */
|
||||
|
||||
@@ -23,7 +23,9 @@ import com.google.common.base.Functions;
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.Result;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
@@ -102,12 +104,22 @@ public class DatastoreTransactionManager implements TransactionManager {
|
||||
|
||||
@Override
|
||||
public void insert(Object entity) {
|
||||
saveEntity(entity);
|
||||
put(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertAll(ImmutableCollection<?> entities) {
|
||||
getOfy().save().entities(entities);
|
||||
putAll(entities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertWithoutBackup(Object entity) {
|
||||
putWithoutBackup(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertAllWithoutBackup(ImmutableCollection<?> entities) {
|
||||
putAllWithoutBackup(entities);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,17 +129,37 @@ public class DatastoreTransactionManager implements TransactionManager {
|
||||
|
||||
@Override
|
||||
public void putAll(ImmutableCollection<?> entities) {
|
||||
getOfy().save().entities(entities);
|
||||
syncIfTransactionless(getOfy().save().entities(entities));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putWithoutBackup(Object entity) {
|
||||
syncIfTransactionless(getOfy().saveWithoutBackup().entities(entity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAllWithoutBackup(ImmutableCollection<?> entities) {
|
||||
syncIfTransactionless(getOfy().saveWithoutBackup().entities(entities));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Object entity) {
|
||||
saveEntity(entity);
|
||||
put(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAll(ImmutableCollection<?> entities) {
|
||||
getOfy().save().entities(entities);
|
||||
putAll(entities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateWithoutBackup(Object entity) {
|
||||
putWithoutBackup(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAllWithoutBackup(ImmutableCollection<?> entities) {
|
||||
putAllWithoutBackup(entities);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -158,6 +190,11 @@ public class DatastoreTransactionManager implements TransactionManager {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T load(T entity) {
|
||||
return ofy().load().entity(entity).now();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ImmutableMap<VKey<? extends T>, T> load(Iterable<? extends VKey<? extends T>> keys) {
|
||||
// Keep track of the Key -> VKey mapping so we can translate them back.
|
||||
@@ -175,13 +212,17 @@ public class DatastoreTransactionManager implements TransactionManager {
|
||||
|
||||
@Override
|
||||
public <T> ImmutableList<T> loadAll(Class<T> clazz) {
|
||||
// We can do a ofy().load().type(clazz), but this doesn't work in a transaction.
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
return ImmutableList.copyOf(getOfy().load().type(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ImmutableList<T> loadAll(Iterable<T> entities) {
|
||||
return ImmutableList.copyOf(getOfy().load().entities(entities).values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(VKey<?> key) {
|
||||
getOfy().delete().key(key.getOfyKey()).now();
|
||||
syncIfTransactionless(getOfy().delete().key(key.getOfyKey()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -192,7 +233,35 @@ public class DatastoreTransactionManager implements TransactionManager {
|
||||
StreamSupport.stream(vKeys.spliterator(), false)
|
||||
.map(VKey::getOfyKey)
|
||||
.collect(toImmutableList());
|
||||
getOfy().delete().keys(list).now();
|
||||
syncIfTransactionless(getOfy().delete().keys(list));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Object entity) {
|
||||
syncIfTransactionless(getOfy().delete().entity(entity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteWithoutBackup(VKey<?> key) {
|
||||
syncIfTransactionless(getOfy().deleteWithoutBackup().key(key.getOfyKey()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteWithoutBackup(Iterable<? extends VKey<?>> keys) {
|
||||
syncIfTransactionless(
|
||||
getOfy()
|
||||
.deleteWithoutBackup()
|
||||
.keys(Streams.stream(keys).map(VKey::getOfyKey).collect(toImmutableList())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteWithoutBackup(Object entity) {
|
||||
syncIfTransactionless(getOfy().deleteWithoutBackup().entity(entity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSessionCache() {
|
||||
getOfy().clearSessionCache();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,7 +278,7 @@ public class DatastoreTransactionManager implements TransactionManager {
|
||||
if (entity instanceof HistoryEntry) {
|
||||
entity = ((HistoryEntry) entity).asHistoryEntry();
|
||||
}
|
||||
getOfy().save().entity(entity);
|
||||
syncIfTransactionless(getOfy().save().entity(entity));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -227,4 +296,19 @@ public class DatastoreTransactionManager implements TransactionManager {
|
||||
private <T> T loadNullable(VKey<T> key) {
|
||||
return toChildHistoryEntryIfPossible(getOfy().load().key(key.getOfyKey()).now());
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given {@link Result} instance synchronously if not in a transaction.
|
||||
*
|
||||
* <p>The {@link Result} instance contains a task that will be executed by Objectify
|
||||
* asynchronously. If it is in a transaction, we don't need to execute the task immediately
|
||||
* because it is guaranteed to be done by the end of the transaction. However, if it is not in a
|
||||
* transaction, we need to execute it in case the following code expects that happens before
|
||||
* themselves.
|
||||
*/
|
||||
private void syncIfTransactionless(Result<?> result) {
|
||||
if (!inTransaction()) {
|
||||
result.now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.model.registry.Registries.assertTldsExist;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableSortedCopy;
|
||||
import static google.registry.util.PasswordUtils.SALT_SUPPLIER;
|
||||
@@ -704,10 +705,18 @@ public class Registrar extends ImmutableObject
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
/** Creates a {@link VKey} for this instance. */
|
||||
public VKey<Registrar> createVKey() {
|
||||
return VKey.create(Registrar.class, clientIdentifier, Key.create(this));
|
||||
}
|
||||
|
||||
/** Creates a {@link VKey} for the given {@code registrarId}. */
|
||||
public static VKey<Registrar> createVKey(String registrarId) {
|
||||
checkArgumentNotNull(registrarId, "registrarId must be specified");
|
||||
return VKey.create(
|
||||
Registrar.class, registrarId, Key.create(getCrossTldKey(), Registrar.class, registrarId));
|
||||
}
|
||||
|
||||
/** A builder for constructing {@link Registrar}, since it is immutable. */
|
||||
public static class Builder extends Buildable.Builder<Registrar> {
|
||||
public Builder() {}
|
||||
@@ -991,8 +1000,7 @@ public class Registrar extends ImmutableObject
|
||||
/** Loads and returns a registrar entity by its client id directly from Datastore. */
|
||||
public static Optional<Registrar> loadByClientId(String clientId) {
|
||||
checkArgument(!Strings.isNullOrEmpty(clientId), "clientId must be specified");
|
||||
return Optional.ofNullable(
|
||||
ofy().load().type(Registrar.class).parent(getCrossTldKey()).id(clientId).now());
|
||||
return transactIfJpaTm(() -> tm().maybeLoad(createVKey(clientId)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,6 +25,7 @@ import static google.registry.model.CacheUtils.memoizeWithShortExpiration;
|
||||
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.ofyOrJpaTm;
|
||||
import static google.registry.util.CollectionUtils.entriesToImmutableMap;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
@@ -59,15 +60,21 @@ public final class Registries {
|
||||
tm().doTransactionless(
|
||||
() -> {
|
||||
ImmutableSet<String> tlds =
|
||||
ofy()
|
||||
.load()
|
||||
.type(Registry.class)
|
||||
.ancestor(getCrossTldKey())
|
||||
.keys()
|
||||
.list()
|
||||
.stream()
|
||||
.map(Key::getName)
|
||||
.collect(toImmutableSet());
|
||||
ofyOrJpaTm(
|
||||
() ->
|
||||
ofy()
|
||||
.load()
|
||||
.type(Registry.class)
|
||||
.ancestor(getCrossTldKey())
|
||||
.keys()
|
||||
.list()
|
||||
.stream()
|
||||
.map(Key::getName)
|
||||
.collect(toImmutableSet()),
|
||||
() ->
|
||||
tm().loadAll(Registry.class).stream()
|
||||
.map(Registry::getTldStr)
|
||||
.collect(toImmutableSet()));
|
||||
return Registry.getAll(tlds).stream()
|
||||
.map(e -> Maps.immutableEntry(e.getTldStr(), e.getTldType()))
|
||||
.collect(entriesToImmutableMap());
|
||||
|
||||
@@ -22,7 +22,6 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Maps.toMap;
|
||||
import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration;
|
||||
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
@@ -61,6 +60,7 @@ import google.registry.model.domain.fee.BaseFee.FeeType;
|
||||
import google.registry.model.domain.fee.Fee;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.ReservedList;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.schema.replay.DatastoreAndSqlEntity;
|
||||
import google.registry.util.Idn;
|
||||
import java.util.Map;
|
||||
@@ -267,28 +267,24 @@ public class Registry extends ImmutableObject implements Buildable, DatastoreAnd
|
||||
public Optional<Registry> load(final String tld) {
|
||||
// Enter a transaction-less context briefly; we don't want to enroll every TLD in
|
||||
// a transaction that might be wrapping this call.
|
||||
return Optional.ofNullable(
|
||||
tm().doTransactionless(
|
||||
() ->
|
||||
ofy()
|
||||
.load()
|
||||
.key(Key.create(getCrossTldKey(), Registry.class, tld))
|
||||
.now()));
|
||||
return tm().doTransactionless(() -> tm().maybeLoad(createVKey(tld)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Optional<Registry>> loadAll(Iterable<? extends String> tlds) {
|
||||
ImmutableMap<String, Key<Registry>> keysMap =
|
||||
toMap(
|
||||
ImmutableSet.copyOf(tlds),
|
||||
tld -> Key.create(getCrossTldKey(), Registry.class, tld));
|
||||
Map<Key<Registry>, Registry> entities =
|
||||
tm().doTransactionless(() -> ofy().load().keys(keysMap.values()));
|
||||
ImmutableMap<String, VKey<Registry>> keysMap =
|
||||
toMap(ImmutableSet.copyOf(tlds), Registry::createVKey);
|
||||
Map<VKey<? extends Registry>, Registry> entities =
|
||||
tm().doTransactionless(() -> tm().load(keysMap.values()));
|
||||
return Maps.transformEntries(
|
||||
keysMap, (k, v) -> Optional.ofNullable(entities.getOrDefault(v, null)));
|
||||
}
|
||||
});
|
||||
|
||||
public static VKey<Registry> createVKey(String tld) {
|
||||
return VKey.create(Registry.class, tld, Key.create(getCrossTldKey(), Registry.class, tld));
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the pricing engine that this TLD uses.
|
||||
*
|
||||
@@ -386,7 +382,7 @@ public class Registry extends ImmutableObject implements Buildable, DatastoreAnd
|
||||
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
|
||||
|
||||
/** The set of reserved lists that are applicable to this registry. */
|
||||
@Column(name = "reserved_list_names", nullable = false)
|
||||
@Column(name = "reserved_list_names")
|
||||
Set<Key<ReservedList>> reservedLists;
|
||||
|
||||
/** Retrieves an ImmutableSet of all ReservedLists associated with this tld. */
|
||||
|
||||
@@ -193,7 +193,7 @@ public class HistoryEntry extends ImmutableObject implements Buildable, Datastor
|
||||
* transaction counts (such as contact or host mutations).
|
||||
*/
|
||||
@Transient // domain-specific
|
||||
Set<DomainTransactionRecord> domainTransactionRecords;
|
||||
protected Set<DomainTransactionRecord> domainTransactionRecords;
|
||||
|
||||
public long getId() {
|
||||
// For some reason, Hibernate throws NPE during some initialization phase if we don't deal with
|
||||
@@ -273,7 +273,8 @@ public class HistoryEntry extends ImmutableObject implements Buildable, Datastor
|
||||
/** This method exists solely to satisfy Hibernate. Use the {@link Builder} instead. */
|
||||
@SuppressWarnings("UnusedMethod")
|
||||
private void setDomainTransactionRecords(Set<DomainTransactionRecord> domainTransactionRecords) {
|
||||
this.domainTransactionRecords = ImmutableSet.copyOf(domainTransactionRecords);
|
||||
this.domainTransactionRecords =
|
||||
domainTransactionRecords == null ? null : ImmutableSet.copyOf(domainTransactionRecords);
|
||||
}
|
||||
|
||||
public static VKey<HistoryEntry> createVKey(Key<HistoryEntry> key) {
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.model.server;
|
||||
|
||||
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
@@ -23,11 +24,13 @@ import com.googlecode.objectify.annotation.Parent;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.model.common.EntityGroupRoot;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
|
||||
/** Pointer to the latest {@link KmsSecretRevision}. */
|
||||
@Entity
|
||||
@ReportedOn
|
||||
public class KmsSecret extends ImmutableObject {
|
||||
public class KmsSecret extends ImmutableObject implements DatastoreEntity {
|
||||
|
||||
/** The unique name of this {@link KmsSecret}. */
|
||||
@Id String name;
|
||||
@@ -45,6 +48,11 @@ public class KmsSecret extends ImmutableObject {
|
||||
return latestRevision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // not persisted in SQL
|
||||
}
|
||||
|
||||
public static KmsSecret create(String name, KmsSecretRevision latestRevision) {
|
||||
KmsSecret instance = new KmsSecret();
|
||||
instance.name = name;
|
||||
|
||||
@@ -17,14 +17,24 @@ package google.registry.model.server;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
import com.googlecode.objectify.annotation.Ignore;
|
||||
import com.googlecode.objectify.annotation.OnLoad;
|
||||
import com.googlecode.objectify.annotation.Parent;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.PostLoad;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
/**
|
||||
* An encrypted value.
|
||||
@@ -35,13 +45,22 @@ import google.registry.model.annotations.ReportedOn;
|
||||
*
|
||||
* <p>The value can be encrypted and decrypted using Cloud KMS.
|
||||
*
|
||||
* <p>Note that the primary key of this entity is {@link #revisionKey}, which is auto-generated by
|
||||
* the database. So, if a retry of insertion happens after the previous attempt unexpectedly
|
||||
* succeeds, we will end up with having two exact same revisions that differ only by revisionKey.
|
||||
* This is fine though, because we only use the revision with the highest revisionKey.
|
||||
*
|
||||
* <p>TODO: remove Datastore-specific fields post-Registry-3.0-migration and rename to KmsSecret.
|
||||
*
|
||||
* @see <a href="https://cloud.google.com/kms/docs/">Google Cloud Key Management Service
|
||||
* Documentation</a>
|
||||
* @see google.registry.keyring.kms.KmsKeyring
|
||||
*/
|
||||
@Entity
|
||||
@ReportedOn
|
||||
public class KmsSecretRevision extends ImmutableObject {
|
||||
@javax.persistence.Entity(name = "KmsSecret")
|
||||
@Table(indexes = {@Index(columnList = "secretName")})
|
||||
public class KmsSecretRevision extends ImmutableObject implements DatastoreEntity, SqlEntity {
|
||||
|
||||
/**
|
||||
* The maximum allowable secret size. Although Datastore allows entities up to 1 MB in size,
|
||||
@@ -49,18 +68,31 @@ public class KmsSecretRevision extends ImmutableObject {
|
||||
*/
|
||||
private static final int MAX_SECRET_SIZE_BYTES = 64 * 1024 * 1024;
|
||||
|
||||
/** The revision of this secret. */
|
||||
@Id long revisionKey;
|
||||
/**
|
||||
* The revision of this secret.
|
||||
*
|
||||
* <p>TODO: change name of the variable to revisionId once we're off Datastore
|
||||
*/
|
||||
@Id
|
||||
@javax.persistence.Id
|
||||
@Column(name = "revisionId")
|
||||
long revisionKey;
|
||||
|
||||
/** The parent {@link KmsSecret} which contains metadata about this {@link KmsSecretRevision}. */
|
||||
@Parent Key<KmsSecret> parent;
|
||||
@Parent @Transient Key<KmsSecret> parent;
|
||||
@Column(nullable = false)
|
||||
@Ignore
|
||||
String secretName;
|
||||
|
||||
/**
|
||||
* The name of the {@code cryptoKeyVersion} associated with this {@link KmsSecretRevision}.
|
||||
*
|
||||
* <p>TODO: change name of the variable to cryptoKeyVersionName once we're off Datastore
|
||||
*
|
||||
* @see <a
|
||||
* href="https://cloud.google.com/kms/docs/reference/rest/v1/projects.locations.keyRings.cryptoKeys.cryptoKeyVersions">projects.locations.keyRings.cryptoKeys.cryptoKeyVersions</a>
|
||||
*/
|
||||
@Column(nullable = false, name = "cryptoKeyVersionName")
|
||||
String kmsCryptoKeyVersionName;
|
||||
|
||||
/**
|
||||
@@ -70,9 +102,11 @@ public class KmsSecretRevision extends ImmutableObject {
|
||||
* @see <a
|
||||
* href="https://cloud.google.com/kms/docs/reference/rest/v1/projects.locations.keyRings.cryptoKeys/encrypt">projects.locations.keyRings.cryptoKeys.encrypt</a>
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
String encryptedValue;
|
||||
|
||||
/** An automatically managed creation timestamp. */
|
||||
@Column(nullable = false)
|
||||
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
|
||||
|
||||
public String getKmsCryptoKeyVersionName() {
|
||||
@@ -83,6 +117,28 @@ public class KmsSecretRevision extends ImmutableObject {
|
||||
return encryptedValue;
|
||||
}
|
||||
|
||||
// When loading from SQL, fill out the Datastore-specific field
|
||||
@PostLoad
|
||||
void postLoad() {
|
||||
parent = Key.create(getCrossTldKey(), KmsSecret.class, secretName);
|
||||
}
|
||||
|
||||
// When loading from Datastore, fill out the SQL-specific field
|
||||
@OnLoad
|
||||
void onLoad() {
|
||||
secretName = parent.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // This is dually-written, as we do not care about history
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
|
||||
return ImmutableList.of(); // This is dually-written, as we do not care about history
|
||||
}
|
||||
|
||||
/** A builder for constructing {@link KmsSecretRevision} entities, since they are immutable. */
|
||||
public static class Builder extends Buildable.Builder<KmsSecretRevision> {
|
||||
|
||||
@@ -108,6 +164,7 @@ public class KmsSecretRevision extends ImmutableObject {
|
||||
*/
|
||||
public Builder setParent(String secretName) {
|
||||
getInstance().parent = Key.create(getCrossTldKey(), KmsSecret.class, secretName);
|
||||
getInstance().secretName = secretName;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.server;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A {@link KmsSecretRevision} DAO for Cloud SQL.
|
||||
*
|
||||
* <p>TODO: Rename this class to KmsSecretDao after migrating to Cloud SQL.
|
||||
*/
|
||||
public class KmsSecretRevisionSqlDao {
|
||||
|
||||
private KmsSecretRevisionSqlDao() {}
|
||||
|
||||
/** Saves the given KMS secret revision. */
|
||||
public static void save(KmsSecretRevision kmsSecretRevision) {
|
||||
checkArgumentNotNull(kmsSecretRevision, "kmsSecretRevision cannot be null");
|
||||
jpaTm().assertInTransaction();
|
||||
jpaTm().put(kmsSecretRevision);
|
||||
}
|
||||
|
||||
/** Returns the latest revision for the secret name given, or absent if nonexistent. */
|
||||
public static Optional<KmsSecretRevision> getLatestRevision(String secretName) {
|
||||
checkArgument(!isNullOrEmpty(secretName), "secretName cannot be null or empty");
|
||||
jpaTm().assertInTransaction();
|
||||
return jpaTm()
|
||||
.getEntityManager()
|
||||
.createQuery(
|
||||
"FROM KmsSecret ks WHERE ks.revisionKey IN (SELECT MAX(revisionKey) FROM "
|
||||
+ "KmsSecret subKs WHERE subKs.secretName = :secretName)",
|
||||
KmsSecretRevision.class)
|
||||
.setParameter("secretName", secretName)
|
||||
.getResultStream()
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,12 @@ import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.MapDifference;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.EmbedMap;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
@@ -41,8 +45,19 @@ import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.annotations.NotBackedUp;
|
||||
import google.registry.model.annotations.NotBackedUp.Reason;
|
||||
import google.registry.model.common.EntityGroupRoot;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import google.registry.util.CollectionUtils;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.persistence.CollectionTable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.Transient;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
@@ -56,35 +71,50 @@ import org.joda.time.DateTime;
|
||||
* order to avoid exceeding the one megabyte max entity size limit, we'll also be sharding that
|
||||
* entity into multiple entities, each entity containing {@value #SHARD_SIZE} rows.
|
||||
*
|
||||
* <p>TODO: We can remove the sharding once we have converted entirely to Cloud SQL storage during
|
||||
* the Registry 3.0 migration. Then, the entire table will be stored conceptually as one entity (in
|
||||
* fact in SignedMarkRevocationList and SignedMarkRevocationEntry tables).
|
||||
*
|
||||
* @see google.registry.tmch.SmdrlCsvParser
|
||||
* @see <a href="http://tools.ietf.org/html/draft-lozano-tmch-func-spec-08#section-6.2">
|
||||
* TMCH functional specifications - SMD Revocation List</a>
|
||||
* @see <a href="http://tools.ietf.org/html/draft-lozano-tmch-func-spec-08#section-6.2">TMCH
|
||||
* functional specifications - SMD Revocation List</a>
|
||||
*/
|
||||
@Entity
|
||||
@javax.persistence.Entity
|
||||
@NotBackedUp(reason = Reason.EXTERNALLY_SOURCED)
|
||||
public class SignedMarkRevocationList extends ImmutableObject {
|
||||
public class SignedMarkRevocationList extends ImmutableObject
|
||||
implements DatastoreEntity, SqlEntity {
|
||||
|
||||
@VisibleForTesting
|
||||
static final int SHARD_SIZE = 10000;
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@VisibleForTesting static final int SHARD_SIZE = 10000;
|
||||
|
||||
/** Common ancestor for queries. */
|
||||
@Parent
|
||||
Key<EntityGroupRoot> parent = getCrossTldKey();
|
||||
@Parent @Transient Key<EntityGroupRoot> parent = getCrossTldKey();
|
||||
|
||||
/** ID for the sharded entity. */
|
||||
@Id
|
||||
long id;
|
||||
@Id @Transient long id;
|
||||
|
||||
@Ignore
|
||||
@javax.persistence.Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
Long revisionId;
|
||||
|
||||
/** Time when this list was last updated, as specified in the first line of the CSV file. */
|
||||
DateTime creationTime;
|
||||
|
||||
/** A map from SMD IDs to revocation time. */
|
||||
@EmbedMap
|
||||
@ElementCollection
|
||||
@CollectionTable(
|
||||
name = "SignedMarkRevocationEntry",
|
||||
joinColumns = @JoinColumn(name = "revisionId", referencedColumnName = "revisionId"))
|
||||
@MapKeyColumn(name = "smdId")
|
||||
@Column(name = "revocationTime", nullable = false)
|
||||
Map</*@MatchesPattern("[0-9]+-[0-9]+")*/ String, DateTime> revokes;
|
||||
|
||||
/** Indicates that this is a shard rather than a "full" list. */
|
||||
@Ignore
|
||||
boolean isShard;
|
||||
@Ignore @Transient boolean isShard;
|
||||
|
||||
/**
|
||||
* A cached supplier that fetches the SMDRL shards from Datastore and recombines them into a
|
||||
@@ -92,32 +122,16 @@ public class SignedMarkRevocationList extends ImmutableObject {
|
||||
*/
|
||||
private static final Supplier<SignedMarkRevocationList> CACHE =
|
||||
memoizeWithShortExpiration(
|
||||
() ->
|
||||
tm()
|
||||
.transactNewReadOnly(
|
||||
() -> {
|
||||
Iterable<SignedMarkRevocationList> shards =
|
||||
ofy()
|
||||
.load()
|
||||
.type(SignedMarkRevocationList.class)
|
||||
.ancestor(getCrossTldKey());
|
||||
DateTime creationTime =
|
||||
isEmpty(shards)
|
||||
? START_OF_TIME
|
||||
: checkNotNull(
|
||||
Iterables.get(shards, 0).creationTime, "creationTime");
|
||||
ImmutableMap.Builder<String, DateTime> revokes =
|
||||
new ImmutableMap.Builder<>();
|
||||
for (SignedMarkRevocationList shard : shards) {
|
||||
revokes.putAll(shard.revokes);
|
||||
checkState(
|
||||
creationTime.equals(shard.creationTime),
|
||||
"Inconsistent creation times: %s vs. %s",
|
||||
creationTime,
|
||||
shard.creationTime);
|
||||
}
|
||||
return create(creationTime, revokes.build());
|
||||
}));
|
||||
() -> {
|
||||
SignedMarkRevocationList datastoreList = loadFromDatastore();
|
||||
// Also load the list from Cloud SQL, compare the two lists, and log if different.
|
||||
try {
|
||||
loadAndCompareCloudSqlList(datastoreList);
|
||||
} catch (Throwable t) {
|
||||
logger.atSevere().withCause(t).log("Error comparing signed mark revocation lists.");
|
||||
}
|
||||
return datastoreList;
|
||||
});
|
||||
|
||||
/** Return a single logical instance that combines all Datastore shards. */
|
||||
public static SignedMarkRevocationList get() {
|
||||
@@ -149,10 +163,39 @@ public class SignedMarkRevocationList extends ImmutableObject {
|
||||
return revokes.size();
|
||||
}
|
||||
|
||||
/** Save this list to Datastore in sharded form. Returns {@code this}. */
|
||||
/** Save this list to Datastore in sharded form and to Cloud SQL. Returns {@code this}. */
|
||||
public SignedMarkRevocationList save() {
|
||||
tm()
|
||||
.transact(
|
||||
saveToDatastore();
|
||||
SignedMarkRevocationListDao.trySave(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Loads the shards from Datastore and combines them into one list. */
|
||||
private static SignedMarkRevocationList loadFromDatastore() {
|
||||
return tm().transactNewReadOnly(
|
||||
() -> {
|
||||
Iterable<SignedMarkRevocationList> shards =
|
||||
ofy().load().type(SignedMarkRevocationList.class).ancestor(getCrossTldKey());
|
||||
DateTime creationTime =
|
||||
isEmpty(shards)
|
||||
? START_OF_TIME
|
||||
: checkNotNull(Iterables.get(shards, 0).creationTime, "creationTime");
|
||||
ImmutableMap.Builder<String, DateTime> revokes = new ImmutableMap.Builder<>();
|
||||
for (SignedMarkRevocationList shard : shards) {
|
||||
revokes.putAll(shard.revokes);
|
||||
checkState(
|
||||
creationTime.equals(shard.creationTime),
|
||||
"Inconsistent creation times: %s vs. %s",
|
||||
creationTime,
|
||||
shard.creationTime);
|
||||
}
|
||||
return create(creationTime, revokes.build());
|
||||
});
|
||||
}
|
||||
|
||||
/** Save this list to Datastore in sharded form. */
|
||||
private SignedMarkRevocationList saveToDatastore() {
|
||||
tm().transact(
|
||||
() -> {
|
||||
ofy()
|
||||
.deleteWithoutBackup()
|
||||
@@ -165,8 +208,7 @@ public class SignedMarkRevocationList extends ImmutableObject {
|
||||
ofy()
|
||||
.saveWithoutBackup()
|
||||
.entities(
|
||||
CollectionUtils.partitionMap(revokes, SHARD_SIZE)
|
||||
.stream()
|
||||
CollectionUtils.partitionMap(revokes, SHARD_SIZE).stream()
|
||||
.map(
|
||||
shardRevokes -> {
|
||||
SignedMarkRevocationList shard = create(creationTime, shardRevokes);
|
||||
@@ -180,6 +222,38 @@ public class SignedMarkRevocationList extends ImmutableObject {
|
||||
return this;
|
||||
}
|
||||
|
||||
private static void loadAndCompareCloudSqlList(SignedMarkRevocationList datastoreList) {
|
||||
// Lifted with some modifications from ClaimsListShard
|
||||
Optional<SignedMarkRevocationList> maybeCloudSqlList =
|
||||
SignedMarkRevocationListDao.getLatestRevision();
|
||||
if (maybeCloudSqlList.isPresent()) {
|
||||
SignedMarkRevocationList cloudSqlList = maybeCloudSqlList.get();
|
||||
MapDifference<String, DateTime> diff =
|
||||
Maps.difference(datastoreList.revokes, cloudSqlList.revokes);
|
||||
if (!diff.areEqual()) {
|
||||
if (diff.entriesDiffering().size() > 10) {
|
||||
logger.atWarning().log(
|
||||
String.format(
|
||||
"Unequal SM revocation lists detected, Cloud SQL list with revision id %d has %d"
|
||||
+ " different records than the current Datastore list.",
|
||||
cloudSqlList.revisionId, diff.entriesDiffering().size()));
|
||||
} else {
|
||||
StringBuilder diffMessage = new StringBuilder("Unequal SM revocation lists detected:\n");
|
||||
diff.entriesDiffering()
|
||||
.forEach(
|
||||
(label, valueDiff) ->
|
||||
diffMessage.append(
|
||||
String.format(
|
||||
"SMD %s has key %s in Datastore and key %s in Cloud SQL.\n",
|
||||
label, valueDiff.leftValue(), valueDiff.rightValue())));
|
||||
logger.atWarning().log(diffMessage.toString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.atWarning().log("Signed mark revocation list in Cloud SQL is empty.");
|
||||
}
|
||||
}
|
||||
|
||||
/** As a safety mechanism, fail if someone tries to save this class directly. */
|
||||
@OnSave
|
||||
void disallowUnshardedSaves() {
|
||||
@@ -188,6 +262,16 @@ public class SignedMarkRevocationList extends ImmutableObject {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // Dually-written every day
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
|
||||
return ImmutableList.of(); // Dually-written every day
|
||||
}
|
||||
|
||||
/** Exception when trying to directly save a {@link SignedMarkRevocationList} without sharding. */
|
||||
public static class UnshardedSaveException extends RuntimeException {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.smd;
|
||||
|
||||
import static google.registry.model.CacheUtils.memoizeWithShortExpiration;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import java.util.Optional;
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
public class SignedMarkRevocationListDao {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final Supplier<Optional<SignedMarkRevocationList>> CACHE =
|
||||
memoizeWithShortExpiration(SignedMarkRevocationListDao::getLatestRevision);
|
||||
|
||||
/** Returns the most recent revision of the {@link SignedMarkRevocationList}, from cache. */
|
||||
public static Optional<SignedMarkRevocationList> getLatestRevisionCached() {
|
||||
return CACHE.get();
|
||||
}
|
||||
|
||||
public static Optional<SignedMarkRevocationList> getLatestRevision() {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
EntityManager em = jpaTm().getEntityManager();
|
||||
Long revisionId =
|
||||
em.createQuery("SELECT MAX(revisionId) FROM SignedMarkRevocationList", Long.class)
|
||||
.getSingleResult();
|
||||
return em.createQuery(
|
||||
"FROM SignedMarkRevocationList smrl LEFT JOIN FETCH smrl.revokes "
|
||||
+ "WHERE smrl.revisionId = :revisionId",
|
||||
SignedMarkRevocationList.class)
|
||||
.setParameter("revisionId", revisionId)
|
||||
.getResultStream()
|
||||
.findFirst();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to save the given {@link SignedMarkRevocationList} into Cloud SQL. If the save fails, the
|
||||
* error will be logged but no exception will be thrown.
|
||||
*
|
||||
* <p>This method is used during the dual-write phase of database migration as Datastore is still
|
||||
* the authoritative database.
|
||||
*/
|
||||
static void trySave(SignedMarkRevocationList signedMarkRevocationList) {
|
||||
try {
|
||||
SignedMarkRevocationListDao.save(signedMarkRevocationList);
|
||||
logger.atInfo().log(
|
||||
"Inserted %,d signed mark revocations into Cloud SQL",
|
||||
signedMarkRevocationList.revokes.size());
|
||||
} catch (Throwable e) {
|
||||
logger.atSevere().withCause(e).log("Error inserting signed mark revocations into Cloud SQL");
|
||||
}
|
||||
}
|
||||
|
||||
private static void save(SignedMarkRevocationList signedMarkRevocationList) {
|
||||
jpaTm().transact(() -> jpaTm().getEntityManager().persist(signedMarkRevocationList));
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ package google.registry.module.frontend;
|
||||
import com.google.monitoring.metrics.MetricReporter;
|
||||
import dagger.Component;
|
||||
import dagger.Lazy;
|
||||
import google.registry.config.CertificateCheckerModule;
|
||||
import google.registry.config.CredentialModule;
|
||||
import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.flows.ServerTridProviderModule;
|
||||
@@ -45,7 +44,6 @@ import javax.inject.Singleton;
|
||||
@Component(
|
||||
modules = {
|
||||
AuthModule.class,
|
||||
CertificateCheckerModule.class,
|
||||
ConfigModule.class,
|
||||
ConsoleConfigModule.class,
|
||||
CredentialModule.class,
|
||||
|
||||
+76
@@ -15,6 +15,7 @@
|
||||
package google.registry.persistence.transaction;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
@@ -25,6 +26,7 @@ import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.persistence.JpaRetries;
|
||||
@@ -238,6 +240,16 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
entities.forEach(this::insert);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertWithoutBackup(Object entity) {
|
||||
insert(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertAllWithoutBackup(ImmutableCollection<?> entities) {
|
||||
insertAll(entities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Object entity) {
|
||||
checkArgumentNotNull(entity, "entity must be specified");
|
||||
@@ -253,6 +265,16 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
entities.forEach(this::put);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putWithoutBackup(Object entity) {
|
||||
put(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAllWithoutBackup(ImmutableCollection<?> entities) {
|
||||
putAll(entities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Object entity) {
|
||||
checkArgumentNotNull(entity, "entity must be specified");
|
||||
@@ -269,6 +291,16 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
entities.forEach(this::update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateWithoutBackup(Object entity) {
|
||||
update(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAllWithoutBackup(ImmutableCollection<?> entities) {
|
||||
updateAll(entities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean exists(VKey<T> key) {
|
||||
checkArgumentNotNull(key, "key must be specified");
|
||||
@@ -315,6 +347,14 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T load(T entity) {
|
||||
checkArgumentNotNull(entity, "entity must be specified");
|
||||
assertInTransaction();
|
||||
return (T)
|
||||
load(VKey.createSql(entity.getClass(), emf.getPersistenceUnitUtil().getIdentifier(entity)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ImmutableMap<VKey<? extends T>, T> load(Iterable<? extends VKey<? extends T>> keys) {
|
||||
checkArgumentNotNull(keys, "keys must be specified");
|
||||
@@ -342,6 +382,11 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
.getResultList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ImmutableList<T> loadAll(Iterable<T> entities) {
|
||||
return Streams.stream(entities).map(this::load).collect(toImmutableList());
|
||||
}
|
||||
|
||||
private int internalDelete(VKey<?> key) {
|
||||
checkArgumentNotNull(key, "key must be specified");
|
||||
assertInTransaction();
|
||||
@@ -366,6 +411,37 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
vKeys.forEach(this::internalDelete);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Object entity) {
|
||||
checkArgumentNotNull(entity, "entity must be specified");
|
||||
assertInTransaction();
|
||||
Object managedEntity = entity;
|
||||
if (!getEntityManager().contains(entity)) {
|
||||
managedEntity = getEntityManager().merge(entity);
|
||||
}
|
||||
getEntityManager().remove(managedEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteWithoutBackup(VKey<?> key) {
|
||||
delete(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteWithoutBackup(Iterable<? extends VKey<?>> keys) {
|
||||
delete(keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteWithoutBackup(Object entity) {
|
||||
delete(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSessionCache() {
|
||||
// This is an intended no-op method as there is no session cache in Postgresql.
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void assertDelete(VKey<T> key) {
|
||||
if (internalDelete(key) != 1) {
|
||||
|
||||
@@ -91,18 +91,90 @@ public interface TransactionManager {
|
||||
/** Persists all new entities in the database, throws exception if any entity already exists. */
|
||||
void insertAll(ImmutableCollection<?> entities);
|
||||
|
||||
/**
|
||||
* Persists a new entity in the database without writing any backup if the underlying database is
|
||||
* Datastore.
|
||||
*
|
||||
* <p>This method is for the sake of keeping a single code path when replacing ofy() with tm() in
|
||||
* the application code. When the method is invoked with Datastore, it won't write the commit log
|
||||
* backup; when invoked with Cloud SQL, it behaves the same as the method which doesn't have
|
||||
* "WithoutBackup" in its method name because it is not necessary to have the backup mechanism in
|
||||
* SQL.
|
||||
*/
|
||||
void insertWithoutBackup(Object entity);
|
||||
|
||||
/**
|
||||
* Persists all new entities in the database without writing any backup if the underlying database
|
||||
* is Datastore.
|
||||
*
|
||||
* <p>This method is for the sake of keeping a single code path when replacing ofy() with tm() in
|
||||
* the application code. When the method is invoked with Datastore, it won't write the commit log
|
||||
* backup; when invoked with Cloud SQL, it behaves the same as the method which doesn't have
|
||||
* "WithoutBackup" in its method name because it is not necessary to have the backup mechanism in
|
||||
* SQL.
|
||||
*/
|
||||
void insertAllWithoutBackup(ImmutableCollection<?> entities);
|
||||
|
||||
/** Persists a new entity or update the existing entity in the database. */
|
||||
void put(Object entity);
|
||||
|
||||
/** Persists all new entities or update the existing entities in the database. */
|
||||
void putAll(ImmutableCollection<?> entities);
|
||||
|
||||
/**
|
||||
* Persists a new entity or update the existing entity in the database without writing any backup
|
||||
* if the underlying database is Datastore.
|
||||
*
|
||||
* <p>This method is for the sake of keeping a single code path when replacing ofy() with tm() in
|
||||
* the application code. When the method is invoked with Datastore, it won't write the commit log
|
||||
* backup; when invoked with Cloud SQL, it behaves the same as the method which doesn't have
|
||||
* "WithoutBackup" in its method name because it is not necessary to have the backup mechanism in
|
||||
* SQL.
|
||||
*/
|
||||
void putWithoutBackup(Object entity);
|
||||
|
||||
/**
|
||||
* Persists all new entities or update the existing entities in the database without writing any
|
||||
* backup if the underlying database is Datastore.
|
||||
*
|
||||
* <p>This method is for the sake of keeping a single code path when replacing ofy() with tm() in
|
||||
* the application code. When the method is invoked with Datastore, it won't write the commit log
|
||||
* backup; when invoked with Cloud SQL, it behaves the same as the method which doesn't have
|
||||
* "WithoutBackup" in its method name because it is not necessary to have the backup mechanism in
|
||||
* SQL.
|
||||
*/
|
||||
void putAllWithoutBackup(ImmutableCollection<?> entities);
|
||||
|
||||
/** Updates an entity in the database, throws exception if the entity does not exist. */
|
||||
void update(Object entity);
|
||||
|
||||
/** Updates all entities in the database, throws exception if any entity does not exist. */
|
||||
void updateAll(ImmutableCollection<?> entities);
|
||||
|
||||
/**
|
||||
* Updates an entity in the database without writing any backup if the underlying database is
|
||||
* Datastore.
|
||||
*
|
||||
* <p>This method is for the sake of keeping a single code path when replacing ofy() with tm() in
|
||||
* the application code. When the method is invoked with Datastore, it won't write the commit log
|
||||
* backup; when invoked with Cloud SQL, it behaves the same as the method which doesn't have
|
||||
* "WithoutBackup" in its method name because it is not necessary to have the backup mechanism in
|
||||
* SQL.
|
||||
*/
|
||||
void updateWithoutBackup(Object entity);
|
||||
|
||||
/**
|
||||
* Updates all entities in the database without writing any backup if the underlying database is
|
||||
* Datastore.
|
||||
*
|
||||
* <p>This method is for the sake of keeping a single code path when replacing ofy() with tm() in
|
||||
* the application code. When the method is invoked with Datastore, it won't write the commit log
|
||||
* backup; when invoked with Cloud SQL, it behaves the same as the method which doesn't have
|
||||
* "WithoutBackup" in its method name because it is not necessary to have the backup mechanism in
|
||||
* SQL.
|
||||
*/
|
||||
void updateAllWithoutBackup(ImmutableCollection<?> entities);
|
||||
|
||||
/** Returns whether the given entity with same ID exists. */
|
||||
boolean exists(Object entity);
|
||||
|
||||
@@ -115,6 +187,11 @@ public interface TransactionManager {
|
||||
/** Loads the entity by its id, throws NoSuchElementException if it doesn't exist. */
|
||||
<T> T load(VKey<T> key);
|
||||
|
||||
/**
|
||||
* Loads the given entity from the database, throws NoSuchElementException if it doesn't exist.
|
||||
*/
|
||||
<T> T load(T entity);
|
||||
|
||||
/**
|
||||
* Loads the set of entities by their key id.
|
||||
*
|
||||
@@ -125,9 +202,38 @@ public interface TransactionManager {
|
||||
/** Loads all entities of the given type, returns empty if there is no such entity. */
|
||||
<T> ImmutableList<T> loadAll(Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Loads all given entities from the database, throws NoSuchElementException if it doesn't exist.
|
||||
*/
|
||||
<T> ImmutableList<T> loadAll(Iterable<T> entities);
|
||||
|
||||
/** Deletes the entity by its id. */
|
||||
void delete(VKey<?> key);
|
||||
|
||||
/** Deletes the set of entities by their key id. */
|
||||
void delete(Iterable<? extends VKey<?>> keys);
|
||||
|
||||
/** Deletes the given entity from the database. */
|
||||
void delete(Object entity);
|
||||
|
||||
/**
|
||||
* Deletes the entity by its id without writing any backup if the underlying database is
|
||||
* Datastore.
|
||||
*/
|
||||
void deleteWithoutBackup(VKey<?> key);
|
||||
|
||||
/**
|
||||
* Deletes the set of entities by their key id without writing any backup if the underlying
|
||||
* database is Datastore.
|
||||
*/
|
||||
void deleteWithoutBackup(Iterable<? extends VKey<?>> keys);
|
||||
|
||||
/**
|
||||
* Deletes the given entity from the database without writing any backup if the underlying
|
||||
* database is Datastore.
|
||||
*/
|
||||
void deleteWithoutBackup(Object entity);
|
||||
|
||||
/** Clears the session cache if the underlying database is Datastore, otherwise it is a no-op. */
|
||||
void clearSessionCache();
|
||||
}
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.transaction;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import google.registry.model.ofy.DatastoreTransactionManager;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/** Utility class that provides supplementary methods for {@link TransactionManager}. */
|
||||
public class TransactionManagerUtil {
|
||||
|
||||
/**
|
||||
* Returns the result of the given {@link Supplier}.
|
||||
*
|
||||
* <p>If {@link TransactionManagerFactory#tm()} returns a {@link JpaTransactionManager} instance,
|
||||
* the {@link Supplier} is executed in a transaction.
|
||||
*/
|
||||
public static <T> T transactIfJpaTm(Supplier<T> supplier) {
|
||||
if (tm() instanceof JpaTransactionManager) {
|
||||
return tm().transact(supplier);
|
||||
} else {
|
||||
return supplier.get();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given {@link Runnable}.
|
||||
*
|
||||
* <p>If {@link TransactionManagerFactory#tm()} returns a {@link JpaTransactionManager} instance,
|
||||
* the {@link Runnable} is executed in a transaction.
|
||||
*/
|
||||
public static void transactIfJpaTm(Runnable runnable) {
|
||||
transactIfJpaTm(
|
||||
() -> {
|
||||
runnable.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes either {@code ofyRunnable} if {@link TransactionManagerFactory#tm()} returns a {@link
|
||||
* JpaTransactionManager} instance, or {@code jpaRunnable} if {@link
|
||||
* TransactionManagerFactory#tm()} returns a {@link DatastoreTransactionManager} instance.
|
||||
*/
|
||||
public static void ofyOrJpaTm(Runnable ofyRunnable, Runnable jpaRunnable) {
|
||||
ofyOrJpaTm(
|
||||
() -> {
|
||||
ofyRunnable.run();
|
||||
return null;
|
||||
},
|
||||
() -> {
|
||||
jpaRunnable.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result from either {@code ofySupplier} if {@link TransactionManagerFactory#tm()}
|
||||
* returns a {@link JpaTransactionManager} instance, or {@code jpaSupplier} if {@link
|
||||
* TransactionManagerFactory#tm()} returns a {@link DatastoreTransactionManager} instance.
|
||||
*/
|
||||
public static <T> T ofyOrJpaTm(Supplier<T> ofySupplier, Supplier<T> jpaSupplier) {
|
||||
if (tm() instanceof DatastoreTransactionManager) {
|
||||
return ofySupplier.get();
|
||||
} else if (tm() instanceof JpaTransactionManager) {
|
||||
return jpaSupplier.get();
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Expected tm() to be DatastoreTransactionManager or JpaTransactionManager, but got "
|
||||
+ tm().getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given {@link Runnable} if {@link TransactionManagerFactory#tm()} returns a {@link
|
||||
* DatastoreTransactionManager} instance, otherwise does nothing.
|
||||
*/
|
||||
public static void ofyTmOrDoNothing(Runnable ofyRunnable) {
|
||||
if (tm() instanceof DatastoreTransactionManager) {
|
||||
ofyRunnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result from the given {@link Supplier} if {@link TransactionManagerFactory#tm()}
|
||||
* returns a {@link DatastoreTransactionManager} instance, otherwise returns null.
|
||||
*/
|
||||
public static <T> T ofyTmOrDoNothing(Supplier<T> ofySupplier) {
|
||||
if (tm() instanceof DatastoreTransactionManager) {
|
||||
return ofySupplier.get();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private TransactionManagerUtil() {}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.flows.certs.CertificateChecker;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.registry.Registry;
|
||||
@@ -38,7 +39,6 @@ import google.registry.tools.params.OptionalLongParameter;
|
||||
import google.registry.tools.params.OptionalPhoneNumberParameter;
|
||||
import google.registry.tools.params.OptionalStringParameter;
|
||||
import google.registry.tools.params.PathParameter;
|
||||
import google.registry.util.CertificateChecker;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
+17
-67
@@ -19,42 +19,22 @@ import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.appengine.api.datastore.KeyFactory;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.io.Files;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import google.registry.util.TypeUtils.TypeInstantiator;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command to resave entities with a unique id.
|
||||
*
|
||||
* <p>This command is used to address the duplicate id issue we found for certain {@link
|
||||
* BillingEvent.OneTime} entities. The command reassigns an application wide unique id to the
|
||||
* problematic entity and resaves it, it also resaves the entity having reference to the problematic
|
||||
* entity with the updated id.
|
||||
*
|
||||
* <p>To use this command, you will need to provide the path to a file containing a list of strings
|
||||
* representing the literal of Objectify key for the problematic entities. An example key literal
|
||||
* is:
|
||||
*
|
||||
* <pre>
|
||||
* "DomainBase", "111111-TEST", "HistoryEntry", 2222222, "OneTime", 3333333
|
||||
* </pre>
|
||||
*
|
||||
* <p>Note that the double quotes are part of the key literal. The key literal can be retrieved from
|
||||
* the column <code>__key__.path</code> in BigQuery.
|
||||
*/
|
||||
@Parameters(separators = " =", commandDescription = "Resave entities with a unique id.")
|
||||
public class ResaveEntitiesWithUniqueIdCommand extends MutatingCommand {
|
||||
/** Base Command to dedupe entities with duplicate IDs. */
|
||||
abstract class DedupeEntityIdsCommand<T> extends MutatingCommand {
|
||||
|
||||
@Parameter(
|
||||
names = "--key_paths_file",
|
||||
@@ -66,7 +46,9 @@ public class ResaveEntitiesWithUniqueIdCommand extends MutatingCommand {
|
||||
|
||||
@NonFinalForTesting private static InputStream stdin = System.in;
|
||||
|
||||
private String keyChangeMessage;
|
||||
private StringBuilder changeMessage = new StringBuilder();
|
||||
|
||||
abstract void dedupe(T entity);
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
@@ -85,8 +67,9 @@ public class ResaveEntitiesWithUniqueIdCommand extends MutatingCommand {
|
||||
keyPathsFile == null ? "STDIN" : "File " + keyPathsFile.getAbsolutePath()));
|
||||
continue;
|
||||
}
|
||||
if (entity instanceof BillingEvent.OneTime) {
|
||||
resaveBillingEvent((BillingEvent.OneTime) entity);
|
||||
Class<T> clazz = new TypeInstantiator<T>(getClass()) {}.getExactType();
|
||||
if (clazz.isInstance(entity)) {
|
||||
dedupe((T) entity);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported entity key: " + untypedKey);
|
||||
}
|
||||
@@ -96,37 +79,19 @@ public class ResaveEntitiesWithUniqueIdCommand extends MutatingCommand {
|
||||
|
||||
@Override
|
||||
protected void postBatchExecute() {
|
||||
System.out.println(keyChangeMessage);
|
||||
System.out.println(changeMessage);
|
||||
}
|
||||
|
||||
private void deleteOldAndSaveNewEntity(ImmutableObject oldEntity, ImmutableObject newEntity) {
|
||||
void stageEntityKeyChange(ImmutableObject oldEntity, ImmutableObject newEntity) {
|
||||
stageEntityChange(oldEntity, null);
|
||||
stageEntityChange(null, newEntity);
|
||||
appendChangeMessage(
|
||||
String.format(
|
||||
"Changed entity key from: %s to: %s", Key.create(oldEntity), Key.create(newEntity)));
|
||||
}
|
||||
|
||||
private void resaveBillingEvent(BillingEvent.OneTime billingEvent) {
|
||||
Key<BillingEvent> key = Key.create(billingEvent);
|
||||
Key<DomainBase> domainKey = getGrandParentAsDomain(key);
|
||||
DomainBase domain = ofy().load().key(domainKey).now();
|
||||
|
||||
// The BillingEvent.OneTime entity to be resaved should be the billing event created a few
|
||||
// years ago, so they should not be referenced from TransferData and GracePeriod in the domain.
|
||||
assertNotInDomainTransferData(domain, key);
|
||||
domain
|
||||
.getGracePeriods()
|
||||
.forEach(
|
||||
gracePeriod ->
|
||||
checkState(
|
||||
!gracePeriod.getOneTimeBillingEvent().getOfyKey().equals(key),
|
||||
"Entity %s is referenced by a grace period in domain %s",
|
||||
key,
|
||||
domainKey));
|
||||
|
||||
// By setting id to 0L, Buildable.build() will assign an application wide unique id to it.
|
||||
BillingEvent.OneTime uniqIdBillingEvent = billingEvent.asBuilder().setId(0L).build();
|
||||
deleteOldAndSaveNewEntity(billingEvent, uniqIdBillingEvent);
|
||||
keyChangeMessage =
|
||||
String.format("Old Entity Key: %s New Entity Key: %s", key, Key.create(uniqIdBillingEvent));
|
||||
void appendChangeMessage(String message) {
|
||||
changeMessage.append(message);
|
||||
}
|
||||
|
||||
private static boolean isKind(Key<?> key, Class<?> clazz) {
|
||||
@@ -165,7 +130,7 @@ public class ResaveEntitiesWithUniqueIdCommand extends MutatingCommand {
|
||||
return literal.substring(1, literal.length() - 1);
|
||||
}
|
||||
|
||||
private static Key<DomainBase> getGrandParentAsDomain(Key<?> key) {
|
||||
static Key<DomainBase> getGrandParentAsDomain(Key<?> key) {
|
||||
Key<?> grandParent;
|
||||
try {
|
||||
grandParent = key.getParent().getParent();
|
||||
@@ -178,19 +143,4 @@ public class ResaveEntitiesWithUniqueIdCommand extends MutatingCommand {
|
||||
}
|
||||
return (Key<DomainBase>) grandParent;
|
||||
}
|
||||
|
||||
private static void assertNotInDomainTransferData(DomainBase domainBase, Key<?> key) {
|
||||
if (!domainBase.getTransferData().isEmpty()) {
|
||||
domainBase
|
||||
.getTransferData()
|
||||
.getServerApproveEntities()
|
||||
.forEach(
|
||||
entityKey ->
|
||||
checkState(
|
||||
!entityKey.getOfyKey().equals(key),
|
||||
"Entity %s is referenced by the transfer data in domain %s",
|
||||
key,
|
||||
domainBase.createVKey().getOfyKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.OneTime;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
|
||||
/**
|
||||
* Command to dedupe {@link BillingEvent.OneTime} entities having duplicate IDs.
|
||||
*
|
||||
* <p>This command is used to address the duplicate id issue we found for certain {@link
|
||||
* BillingEvent.OneTime} entities. The command reassigns an application wide unique id to the
|
||||
* problematic entity and resaves it, it also resaves the entity having reference to the problematic
|
||||
* entity with the updated id.
|
||||
*
|
||||
* <p>To use this command, you will need to provide the path to a file containing a list of strings
|
||||
* representing the literal of Objectify key for the problematic entities. An example key literal
|
||||
* is:
|
||||
*
|
||||
* <pre>
|
||||
* "DomainBase", "111111-TEST", "HistoryEntry", 2222222, "OneTime", 3333333
|
||||
* </pre>
|
||||
*
|
||||
* <p>Note that the double quotes are part of the key literal. The key literal can be retrieved from
|
||||
* the column <code>__key__.path</code> in BigQuery.
|
||||
*/
|
||||
@Parameters(
|
||||
separators = " =",
|
||||
commandDescription = "Dedupe BillingEvent.OneTime entities with duplicate IDs.")
|
||||
public class DedupeOneTimeBillingEventIdsCommand extends DedupeEntityIdsCommand<OneTime> {
|
||||
|
||||
@Override
|
||||
void dedupe(OneTime entity) {
|
||||
Key<BillingEvent> key = Key.create(entity);
|
||||
Key<DomainBase> domainKey = getGrandParentAsDomain(key);
|
||||
DomainBase domain = ofy().load().key(domainKey).now();
|
||||
|
||||
// The BillingEvent.OneTime entity to be resaved should be the billing event created a few
|
||||
// years ago, so they should not be referenced from TransferData and GracePeriod in the domain.
|
||||
assertNotInDomainTransferData(domain, key);
|
||||
domain
|
||||
.getGracePeriods()
|
||||
.forEach(
|
||||
gracePeriod ->
|
||||
checkState(
|
||||
!gracePeriod.getOneTimeBillingEvent().getOfyKey().equals(key),
|
||||
"Entity %s is referenced by a grace period in domain %s",
|
||||
key,
|
||||
domainKey));
|
||||
|
||||
// By setting id to 0L, Buildable.build() will assign an application wide unique id to it.
|
||||
BillingEvent.OneTime uniqIdBillingEvent = entity.asBuilder().setId(0L).build();
|
||||
stageEntityKeyChange(entity, uniqIdBillingEvent);
|
||||
}
|
||||
|
||||
private static void assertNotInDomainTransferData(DomainBase domainBase, Key<?> key) {
|
||||
if (!domainBase.getTransferData().isEmpty()) {
|
||||
domainBase
|
||||
.getTransferData()
|
||||
.getServerApproveEntities()
|
||||
.forEach(
|
||||
entityKey ->
|
||||
checkState(
|
||||
!entityKey.getOfyKey().equals(key),
|
||||
"Entity %s is referenced by the transfer data in domain %s",
|
||||
key,
|
||||
domainBase.createVKey().getOfyKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.OneTime;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.transfer.DomainTransferData;
|
||||
import google.registry.model.transfer.TransferData.TransferServerApproveEntity;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A command that re-saves the problematic {@link BillingEvent.Recurring} entities with unique IDs.
|
||||
*
|
||||
* <p>This command is used to address the duplicate id issue we found for certain {@link
|
||||
* BillingEvent.Recurring} entities. The command reassigns an application wide unique id to the
|
||||
* problematic entity and resaves it, it also resaves the entity having reference to the problematic
|
||||
* entity with the updated id.
|
||||
*
|
||||
* <p>To use this command, you will need to provide the path to a file containing a list of strings
|
||||
* representing the literal of Objectify key for the problematic entities. An example key literal
|
||||
* is:
|
||||
*
|
||||
* <pre>
|
||||
* "DomainBase", "111111-TEST", "HistoryEntry", 2222222, "Recurring", 3333333
|
||||
* </pre>
|
||||
*
|
||||
* <p>Note that the double quotes are part of the key literal. The key literal can be retrieved from
|
||||
* the column <code>__key__.path</code> in BigQuery.
|
||||
*/
|
||||
@Parameters(
|
||||
separators = " =",
|
||||
commandDescription = "Dedupe BillingEvent.Recurring entities with duplicate IDs.")
|
||||
public class DedupeRecurringBillingEventIdsCommand
|
||||
extends DedupeEntityIdsCommand<BillingEvent.Recurring> {
|
||||
|
||||
@Override
|
||||
void dedupe(Recurring recurring) {
|
||||
// Loads the associated DomainBase and BillingEvent.OneTime entities that
|
||||
// may have reference to this BillingEvent.Recurring entity.
|
||||
Key<DomainBase> domainKey = getGrandParentAsDomain(Key.create(recurring));
|
||||
DomainBase domain = ofy().load().key(domainKey).now();
|
||||
List<BillingEvent.OneTime> oneTimes =
|
||||
ofy().load().type(BillingEvent.OneTime.class).ancestor(domainKey).list();
|
||||
|
||||
VKey<Recurring> oldRecurringVKey = recurring.createVKey();
|
||||
// By setting id to 0L, Buildable.build() will assign an application wide unique id to it.
|
||||
Recurring uniqIdRecurring = recurring.asBuilder().setId(0L).build();
|
||||
VKey<Recurring> newRecurringVKey = uniqIdRecurring.createVKey();
|
||||
|
||||
// After having the unique id for the BillingEvent.Recurring entity, we also need to
|
||||
// update the references in other entities to point to the new BillingEvent.Recurring
|
||||
// entity.
|
||||
updateReferenceInOneTimeBillingEvent(oneTimes, oldRecurringVKey, newRecurringVKey);
|
||||
updateReferenceInDomain(domain, oldRecurringVKey, newRecurringVKey);
|
||||
|
||||
stageEntityKeyChange(recurring, uniqIdRecurring);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resaves {@link BillingEvent.OneTime} entities with updated {@link
|
||||
* BillingEvent.OneTime#cancellationMatchingBillingEvent}.
|
||||
*
|
||||
* <p>{@link BillingEvent.OneTime#cancellationMatchingBillingEvent} is a {@link VKey} to a {@link
|
||||
* BillingEvent.Recurring} entity. So, if the {@link BillingEvent.Recurring} entity gets a new key
|
||||
* by changing its id, we need to update {@link
|
||||
* BillingEvent.OneTime#cancellationMatchingBillingEvent} as well.
|
||||
*/
|
||||
private void updateReferenceInOneTimeBillingEvent(
|
||||
List<OneTime> oneTimes, VKey<Recurring> oldRecurringVKey, VKey<Recurring> newRecurringVKey) {
|
||||
oneTimes.forEach(
|
||||
oneTime -> {
|
||||
if (oneTime.getCancellationMatchingBillingEvent() != null
|
||||
&& oneTime.getCancellationMatchingBillingEvent().equals(oldRecurringVKey)) {
|
||||
BillingEvent.OneTime updatedOneTime =
|
||||
oneTime.asBuilder().setCancellationMatchingBillingEvent(newRecurringVKey).build();
|
||||
stageEntityChange(oneTime, updatedOneTime);
|
||||
appendChangeMessage(
|
||||
String.format(
|
||||
"Changed cancellationMatchingBillingEvent in entity %s from %s to %s\n",
|
||||
oneTime.createVKey().getOfyKey(),
|
||||
oneTime.getCancellationMatchingBillingEvent().getOfyKey(),
|
||||
updatedOneTime.getCancellationMatchingBillingEvent().getOfyKey()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resaves {@link DomainBase} entity with updated references to {@link BillingEvent.Recurring}
|
||||
* entity.
|
||||
*
|
||||
* <p>The following 4 fields in the domain entity can be or have a reference to this
|
||||
* BillingEvent.Recurring entity, so we need to check them and replace them with the new entity
|
||||
* when necessary:
|
||||
*
|
||||
* <ol>
|
||||
* <li>domain.autorenewBillingEvent, see {@link DomainBase#autorenewBillingEvent}
|
||||
* <li>domain.transferData.serverApproveAutorenewEvent, see {@link
|
||||
* DomainTransferData#serverApproveAutorenewEvent}
|
||||
* <li>domain.transferData.serverApproveEntities, see {@link
|
||||
* DomainTransferData#serverApproveEntities}
|
||||
* <li>domain.gracePeriods.billingEventRecurring, see {@link GracePeriod#billingEventRecurring}
|
||||
* </ol>
|
||||
*/
|
||||
private void updateReferenceInDomain(
|
||||
DomainBase domain, VKey<Recurring> oldRecurringVKey, VKey<Recurring> newRecurringVKey) {
|
||||
DomainBase.Builder domainBuilder = domain.asBuilder();
|
||||
StringBuilder domainChange =
|
||||
new StringBuilder(
|
||||
String.format(
|
||||
"Resaved domain %s with following changes:\n", domain.createVKey().getOfyKey()));
|
||||
|
||||
if (domain.getAutorenewBillingEvent() != null
|
||||
&& domain.getAutorenewBillingEvent().equals(oldRecurringVKey)) {
|
||||
domainBuilder.setAutorenewBillingEvent(newRecurringVKey);
|
||||
domainChange.append(
|
||||
String.format(
|
||||
" Changed autorenewBillingEvent from %s to %s.\n",
|
||||
oldRecurringVKey, newRecurringVKey));
|
||||
}
|
||||
|
||||
if (domain.getTransferData().getServerApproveAutorenewEvent() != null
|
||||
&& domain.getTransferData().getServerApproveAutorenewEvent().equals(oldRecurringVKey)) {
|
||||
Set<VKey<? extends TransferServerApproveEntity>> serverApproveEntities =
|
||||
Sets.union(
|
||||
Sets.difference(
|
||||
domain.getTransferData().getServerApproveEntities(),
|
||||
ImmutableSet.of(oldRecurringVKey)),
|
||||
ImmutableSet.of(newRecurringVKey));
|
||||
domainBuilder.setTransferData(
|
||||
domain
|
||||
.getTransferData()
|
||||
.asBuilder()
|
||||
.setServerApproveEntities(ImmutableSet.copyOf(serverApproveEntities))
|
||||
.setServerApproveAutorenewEvent(newRecurringVKey)
|
||||
.build());
|
||||
domainChange.append(
|
||||
String.format(
|
||||
" Changed transferData.serverApproveAutoRenewEvent from %s to %s.\n",
|
||||
oldRecurringVKey, newRecurringVKey));
|
||||
domainChange.append(
|
||||
String.format(
|
||||
" Changed transferData.serverApproveEntities to remove %s and add %s.\n",
|
||||
oldRecurringVKey, newRecurringVKey));
|
||||
}
|
||||
|
||||
ImmutableSet<GracePeriod> updatedGracePeriod =
|
||||
domain.getGracePeriods().stream()
|
||||
.map(
|
||||
gracePeriod ->
|
||||
gracePeriod.getRecurringBillingEvent().equals(oldRecurringVKey)
|
||||
? gracePeriod.cloneWithRecurringBillingEvent(newRecurringVKey)
|
||||
: gracePeriod)
|
||||
.collect(toImmutableSet());
|
||||
if (!updatedGracePeriod.equals(domain.getGracePeriods())) {
|
||||
domainBuilder.setGracePeriods(updatedGracePeriod);
|
||||
domainChange.append(
|
||||
String.format(
|
||||
" Changed gracePeriods to remove %s and add %s.\n",
|
||||
oldRecurringVKey, newRecurringVKey));
|
||||
}
|
||||
|
||||
DomainBase updatedDomain = domainBuilder.build();
|
||||
if (!updatedDomain.equals(domain)) {
|
||||
stageEntityChange(domain, updatedDomain);
|
||||
appendChangeMessage(domainChange.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,8 @@ public final class RegistryTool {
|
||||
.put("create_reserved_list", CreateReservedListCommand.class)
|
||||
.put("create_tld", CreateTldCommand.class)
|
||||
.put("curl", CurlCommand.class)
|
||||
.put("dedupe_one_time_billing_event_ids", DedupeOneTimeBillingEventIdsCommand.class)
|
||||
.put("dedupe_recurring_billing_event_ids", DedupeRecurringBillingEventIdsCommand.class)
|
||||
.put("delete_allocation_tokens", DeleteAllocationTokensCommand.class)
|
||||
.put("delete_domain", DeleteDomainCommand.class)
|
||||
.put("delete_host", DeleteHostCommand.class)
|
||||
@@ -99,7 +101,6 @@ public final class RegistryTool {
|
||||
.put("remove_ip_address", RemoveIpAddressCommand.class)
|
||||
.put("renew_domain", RenewDomainCommand.class)
|
||||
.put("resave_entities", ResaveEntitiesCommand.class)
|
||||
.put("resave_entities_with_unique_id", ResaveEntitiesWithUniqueIdCommand.class)
|
||||
.put("resave_environment_entities", ResaveEnvironmentEntitiesCommand.class)
|
||||
.put("resave_epp_resource", ResaveEppResourceCommand.class)
|
||||
.put("send_escrow_report_to_icann", SendEscrowReportToIcannCommand.class)
|
||||
|
||||
@@ -20,7 +20,6 @@ import dagger.Lazy;
|
||||
import google.registry.batch.BatchModule;
|
||||
import google.registry.beam.initsql.BeamJpaModule;
|
||||
import google.registry.bigquery.BigqueryModule;
|
||||
import google.registry.config.CertificateCheckerModule;
|
||||
import google.registry.config.CredentialModule.LocalCredentialJson;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryConfig.ConfigModule;
|
||||
@@ -61,7 +60,6 @@ import javax.inject.Singleton;
|
||||
BatchModule.class,
|
||||
BeamJpaModule.class,
|
||||
BigqueryModule.class,
|
||||
CertificateCheckerModule.class,
|
||||
ConfigModule.class,
|
||||
CloudDnsWriterModule.class,
|
||||
DatastoreAdminModule.class,
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.flows.certs.CertificateChecker;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarContact;
|
||||
import google.registry.model.registrar.RegistrarContact.Type;
|
||||
@@ -56,7 +57,6 @@ import google.registry.ui.forms.FormFieldException;
|
||||
import google.registry.ui.server.RegistrarFormFields;
|
||||
import google.registry.ui.server.SendEmailUtils;
|
||||
import google.registry.util.AppEngineServiceUtils;
|
||||
import google.registry.util.CertificateChecker;
|
||||
import google.registry.util.CollectionUtils;
|
||||
import google.registry.util.DiffUtils;
|
||||
import java.util.HashSet;
|
||||
@@ -336,10 +336,7 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
|
||||
*/
|
||||
private boolean validateCertificate(String existingCertificate, String certificateString) {
|
||||
if ((existingCertificate == null) || !existingCertificate.equals(certificateString)) {
|
||||
// TODO(sarhabot): remove this check after November 1, 2020
|
||||
if (tm().getTransactionTime().isAfter(DateTime.parse("2020-11-01T00:00:00Z"))) {
|
||||
certificateChecker.validateCertificate(certificateString);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<class>google.registry.model.domain.DomainHistory</class>
|
||||
<class>google.registry.model.domain.GracePeriod</class>
|
||||
<class>google.registry.model.domain.secdns.DelegationSignerData</class>
|
||||
<class>google.registry.model.domain.secdns.DomainDsDataHistory</class>
|
||||
<class>google.registry.model.domain.token.AllocationToken</class>
|
||||
<class>google.registry.model.host.HostHistory</class>
|
||||
<class>google.registry.model.host.HostResource</class>
|
||||
@@ -61,6 +62,8 @@
|
||||
<class>google.registry.model.registry.Registry</class>
|
||||
<class>google.registry.model.reporting.DomainTransactionRecord</class>
|
||||
<class>google.registry.model.reporting.Spec11ThreatMatch</class>
|
||||
<class>google.registry.model.server.KmsSecretRevision</class>
|
||||
<class>google.registry.model.smd.SignedMarkRevocationList</class>
|
||||
<class>google.registry.model.tmch.ClaimsListShard</class>
|
||||
<class>google.registry.persistence.transaction.TransactionEntity</class>
|
||||
<class>google.registry.schema.cursor.Cursor</class>
|
||||
|
||||
+1
-2
@@ -480,8 +480,7 @@ public class ExpandRecurringBillingEventsActionTest
|
||||
.setSyntheticCreationTime(testTime)
|
||||
.build());
|
||||
}
|
||||
assertBillingEventsForResource(
|
||||
domain, Iterables.toArray(expectedEvents, BillingEvent.class));
|
||||
assertBillingEventsForResource(domain, Iterables.toArray(expectedEvents, BillingEvent.class));
|
||||
assertCursorAt(testTime);
|
||||
}
|
||||
|
||||
|
||||
@@ -805,30 +805,35 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
|
||||
// As the losing registrar, read the request poll message, and then ack it.
|
||||
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
|
||||
String messageId = "1-C-EXAMPLE-20-26-2001";
|
||||
assertThatCommand("poll.xml")
|
||||
.atTime("2001-01-01T00:01:00Z")
|
||||
.hasResponse("poll_response_domain_transfer_request.xml");
|
||||
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-C-EXAMPLE-17-23-2001"))
|
||||
.hasResponse("poll_response_domain_transfer_request.xml", ImmutableMap.of("ID", messageId));
|
||||
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", messageId))
|
||||
.atTime("2001-01-01T00:01:00Z")
|
||||
.hasResponse("poll_ack_response_empty.xml");
|
||||
|
||||
// Five days in the future, expect a server approval poll message to the loser, and ack it.
|
||||
messageId = "1-C-EXAMPLE-20-25-2001";
|
||||
assertThatCommand("poll.xml")
|
||||
.atTime("2001-01-06T00:01:00Z")
|
||||
.hasResponse("poll_response_domain_transfer_server_approve_loser.xml");
|
||||
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-C-EXAMPLE-17-22-2001"))
|
||||
.hasResponse(
|
||||
"poll_response_domain_transfer_server_approve_loser.xml",
|
||||
ImmutableMap.of("ID", messageId));
|
||||
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", messageId))
|
||||
.atTime("2001-01-06T00:01:00Z")
|
||||
.hasResponse("poll_ack_response_empty.xml");
|
||||
assertThatLogoutSucceeds();
|
||||
|
||||
// Also expect a server approval poll message to the winner, with the transfer request trid.
|
||||
messageId = "1-C-EXAMPLE-20-24-2001";
|
||||
assertThatLoginSucceeds("TheRegistrar", "password2");
|
||||
assertThatCommand("poll.xml")
|
||||
.atTime("2001-01-06T00:02:00Z")
|
||||
.hasResponse(
|
||||
"poll_response_domain_transfer_server_approve_winner.xml",
|
||||
ImmutableMap.of("SERVER_TRID", transferRequestTrid));
|
||||
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-C-EXAMPLE-17-21-2001"))
|
||||
ImmutableMap.of("SERVER_TRID", transferRequestTrid, "ID", messageId));
|
||||
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", messageId))
|
||||
.atTime("2001-01-06T00:02:00Z")
|
||||
.hasResponse("poll_ack_response_empty.xml");
|
||||
assertThatLogoutSucceeds();
|
||||
|
||||
+11
-56
@@ -12,19 +12,22 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.util;
|
||||
package google.registry.flows.certs;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.util.CertificateChecker.CertificateViolation.ALGORITHM_CONSTRAINED;
|
||||
import static google.registry.util.CertificateChecker.CertificateViolation.EXPIRED;
|
||||
import static google.registry.util.CertificateChecker.CertificateViolation.NOT_YET_VALID;
|
||||
import static google.registry.util.CertificateChecker.CertificateViolation.RSA_KEY_LENGTH_TOO_SHORT;
|
||||
import static google.registry.util.CertificateChecker.CertificateViolation.VALIDITY_LENGTH_TOO_LONG;
|
||||
import static google.registry.flows.certs.CertificateChecker.CertificateViolation.ALGORITHM_CONSTRAINED;
|
||||
import static google.registry.flows.certs.CertificateChecker.CertificateViolation.EXPIRED;
|
||||
import static google.registry.flows.certs.CertificateChecker.CertificateViolation.NOT_YET_VALID;
|
||||
import static google.registry.flows.certs.CertificateChecker.CertificateViolation.RSA_KEY_LENGTH_TOO_SHORT;
|
||||
import static google.registry.flows.certs.CertificateChecker.CertificateViolation.VALIDITY_LENGTH_TOO_LONG;
|
||||
import static google.registry.testing.CertificateSamples.SAMPLE_CERT;
|
||||
import static google.registry.testing.CertificateSamples.SAMPLE_CERT3;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.SelfSignedCaCertificate;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.X509Certificate;
|
||||
@@ -36,54 +39,6 @@ import org.junit.jupiter.api.Test;
|
||||
class CertificateCheckerTest {
|
||||
|
||||
private static final String SSL_HOST = "www.example.tld";
|
||||
private static final String GOOD_CERTIFICATE =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
+ "MIIDyzCCArOgAwIBAgIUJnhiVrxAxgwkLJzHPm1w/lBoNs4wDQYJKoZIhvcNAQEL\n"
|
||||
+ "BQAwdTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldyBZb3JrMREwDwYDVQQHDAhO\n"
|
||||
+ "ZXcgWW9yazEPMA0GA1UECgwGR29vZ2xlMR0wGwYDVQQLDBRkb21haW4tcmVnaXN0\n"
|
||||
+ "cnktdGVzdDEQMA4GA1UEAwwHY2xpZW50MTAeFw0yMDEwMTIxNzU5NDFaFw0yMTA0\n"
|
||||
+ "MzAxNzU5NDFaMHUxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazERMA8G\n"
|
||||
+ "A1UEBwwITmV3IFlvcmsxDzANBgNVBAoMBkdvb2dsZTEdMBsGA1UECwwUZG9tYWlu\n"
|
||||
+ "LXJlZ2lzdHJ5LXRlc3QxEDAOBgNVBAMMB2NsaWVudDEwggEiMA0GCSqGSIb3DQEB\n"
|
||||
+ "AQUAA4IBDwAwggEKAoIBAQC0msirO7kXyGEC93stsNYGc02Z77Q2qfHFwaGYkUG8\n"
|
||||
+ "QvOF5SWN+jwTo5Td6Jj26A26a8MLCtK45TCBuMRNcUsHhajhT19ocphO20iY3zhi\n"
|
||||
+ "ycwV1id0iwME4kPd1m57BELRE9tUPOxF81/JQXdR1fwT5KRVHYRDWZhaZ5aBmlZY\n"
|
||||
+ "3t/H9Ly0RBYyApkMaGs3nlb94OOug6SouUfRt02S59ja3wsE2SVF/Eui647OXP7O\n"
|
||||
+ "QdYXofxuqLoNkE8EnAdl43/enGLiCIVd0G2lABibFF+gbxTtfgbg7YtfUZJdL+Mb\n"
|
||||
+ "RAcAtuLXEamNQ9H63JgVF16PlQVCDz2XyI3uCfPpDDiBAgMBAAGjUzBRMB0GA1Ud\n"
|
||||
+ "DgQWBBQ26bWk8qfEBjXs/xZ4m8JZyalnITAfBgNVHSMEGDAWgBQ26bWk8qfEBjXs\n"
|
||||
+ "/xZ4m8JZyalnITAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAZ\n"
|
||||
+ "VcsgslBKanKOieJ5ik2d9qzOMXKfBuWPRFWbkC3t9i5awhHqnGAaj6nICnnMZIyt\n"
|
||||
+ "rdx5lZW5aaQyf0EP/90JAA8Xmty4A6MXmEjQAMiCOpP3A7eeS6Xglgi8IOZl4/bg\n"
|
||||
+ "LonW62TUkilo5IiFt/QklFTeHIjXB+OvA8+2Quqyd+zp7v6KnhXjvaomim78DhwE\n"
|
||||
+ "0PIUnjmiRpGpHfTVioTdfhPHZ2Y93Y8K7juL93sQog9aBu5m9XRJCY6wGyWPE83i\n"
|
||||
+ "kmLfGzjcnaJ6kqCd9xQRFZ0JwHmGlkAQvFoeengbNUqSyjyVgsOoNkEsrWwe/JFO\n"
|
||||
+ "iqBvjEhJlvRoefvkdR98\n"
|
||||
+ "-----END CERTIFICATE-----\n";
|
||||
private static final String BAD_CERTIFICATE =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
+ "MIIDvTCCAqWgAwIBAgIJANoEy6mYwalPMA0GCSqGSIb3DQEBCwUAMHUxCzAJBgNV\n"
|
||||
+ "BAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazERMA8GA1UEBwwITmV3IFlvcmsxDzAN\n"
|
||||
+ "BgNVBAoMBkdvb2dsZTEdMBsGA1UECwwUZG9tYWluLXJlZ2lzdHJ5LXRlc3QxEDAO\n"
|
||||
+ "BgNVBAMMB2NsaWVudDIwHhcNMTUwODI2MTkyODU3WhcNNDMwMTExMTkyODU3WjB1\n"
|
||||
+ "MQswCQYDVQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxETAPBgNVBAcMCE5ldyBZ\n"
|
||||
+ "b3JrMQ8wDQYDVQQKDAZHb29nbGUxHTAbBgNVBAsMFGRvbWFpbi1yZWdpc3RyeS10\n"
|
||||
+ "ZXN0MRAwDgYDVQQDDAdjbGllbnQyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n"
|
||||
+ "CgKCAQEAw2FtuDyoR+rUJHp6k7KwaoHGHPV1xnC8IpG9O0SZubOXrFrnBHggBsbu\n"
|
||||
+ "+DsknbHXjmoihSFFem0KQqJg5y34aDAHXQV3iqa7nDfb1x4oc5voVz9gqjdmGKNm\n"
|
||||
+ "WF4MTIPNMu8KY52M852mMCxODK+6MZYp7wCmVa63KdCm0bW/XsLgoA/+FVGwKLhf\n"
|
||||
+ "UqFzt10Cf+87zl4VHrSaJqcHBYM6yAO5lvkr5VC6g8rRQ+dJ+pBT2D99YpSF1aFc\n"
|
||||
+ "rWbBreIypixZAnXm/Xoogu6RnohS29VCJp2dXFAJmKXGwyKNQFXfEKxZBaBi8uKH\n"
|
||||
+ "XF459795eyF9xHgSckEgu7jZlxOk6wIDAQABo1AwTjAdBgNVHQ4EFgQUv26AsQyc\n"
|
||||
+ "kLOjkhqcFLOuueB33l4wHwYDVR0jBBgwFoAUv26AsQyckLOjkhqcFLOuueB33l4w\n"
|
||||
+ "DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEANBuV+QDISSnGAEHKbR40\n"
|
||||
+ "zUYdOjdZ399zcFNqTSPHwmE0Qu8pbmXhofpBfjzrcv0tkVbhSLYnT22qhx7aDmhb\n"
|
||||
+ "bOS8CeVYCwl5eiDTkJly3pRZLzJpy+UT5z8SPxO3MrTqn+wuj0lBpWRTBCWYAUpr\n"
|
||||
+ "IFRmgVB3IwVb60UIuxhmuk8TVss2SzNrdhdt36eAIPJ0RWEb0KHYHi35Y6lt4f+t\n"
|
||||
+ "iVk+ZR0cCbHUs7Q1RqREXHd/ICuMRLY/MsadVQ9WDqVOridh198X/OIqdx/p9kvJ\n"
|
||||
+ "1R80jDcVGNhYVXLmHu4ho4xrOaliSYvUJSCmaaSEGVZ/xE5PI7S6A8RMdj0iXLSt\n"
|
||||
+ "Bg==\n"
|
||||
+ "-----END CERTIFICATE-----\n";
|
||||
|
||||
private FakeClock fakeClock = new FakeClock();
|
||||
private CertificateChecker certificateChecker =
|
||||
@@ -241,8 +196,8 @@ class CertificateCheckerTest {
|
||||
@Test
|
||||
void test_checkCertificate_validCertificateString() throws Exception {
|
||||
fakeClock.setTo(DateTime.parse("2020-11-01T00:00:00Z"));
|
||||
assertThat(certificateChecker.checkCertificate(GOOD_CERTIFICATE)).isEmpty();
|
||||
assertThat(certificateChecker.checkCertificate(BAD_CERTIFICATE))
|
||||
assertThat(certificateChecker.checkCertificate(SAMPLE_CERT3)).isEmpty();
|
||||
assertThat(certificateChecker.checkCertificate(SAMPLE_CERT))
|
||||
.containsExactly(VALIDITY_LENGTH_TOO_LONG);
|
||||
}
|
||||
|
||||
@@ -32,9 +32,11 @@ import com.googlecode.objectify.annotation.Id;
|
||||
import google.registry.schema.replay.EntityTest.EntityForTesting;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -320,4 +322,40 @@ public class ImmutableObjectTest {
|
||||
root.set = ImmutableSet.of(Key.create(persistResource(ValueObject.create(1, "foo"))));
|
||||
assertThat(root.toHydratedString()).contains("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInsignificantFields() {
|
||||
HasInsignificantFields instance1 =
|
||||
HasInsignificantFields.create("significant", "insignificant");
|
||||
HasInsignificantFields instance2 = HasInsignificantFields.create("significant", "other");
|
||||
assertThat(instance1).isEqualTo(instance2);
|
||||
|
||||
// The hash code test test is implicit in "equals", it is added here just for clarity.
|
||||
assertThat(instance1.hashCode()).isEqualTo(instance2.hashCode());
|
||||
assertThat(instance1.toString()).matches(
|
||||
"(?s)HasInsignificantFields (.*): \\{\\s*significant=significant\\s*\\}\\s*");
|
||||
}
|
||||
|
||||
static class HasInsignificantFields extends ImmutableObject {
|
||||
String significant;
|
||||
String insignificant;
|
||||
|
||||
static HasInsignificantFields create(String significant, String insignificant) {
|
||||
HasInsignificantFields instance = new HasInsignificantFields();
|
||||
instance.significant = significant;
|
||||
instance.insignificant = insignificant;
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<Field, Object> getSignificantFields() {
|
||||
Map<Field, Object> result = new LinkedHashMap();
|
||||
for (Map.Entry<Field, Object> entry : ModelUtils.getFieldValues(this).entrySet()) {
|
||||
if (!entry.getKey().getName().equals("insignificant")) {
|
||||
result.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,12 @@ package google.registry.model.billing;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.ofyTmOrDoNothing;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
import static google.registry.testing.DatastoreHelper.createTld;
|
||||
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatastoreHelper.persistResource;
|
||||
import static google.registry.testing.SqlHelper.saveRegistrar;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
@@ -39,13 +40,17 @@ import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import google.registry.testing.TestOfyOnly;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link BillingEvent}. */
|
||||
@DualDatabaseTest
|
||||
public class BillingEventTest extends EntityTestCase {
|
||||
private final DateTime now = DateTime.now(UTC);
|
||||
|
||||
@@ -67,16 +72,26 @@ public class BillingEventTest extends EntityTestCase {
|
||||
void setUp() {
|
||||
createTld("tld");
|
||||
domain = persistActiveDomain("foo.tld");
|
||||
historyEntry = persistResource(
|
||||
new HistoryEntry.Builder()
|
||||
.setParent(domain)
|
||||
.setModificationTime(now)
|
||||
.build());
|
||||
historyEntry2 = persistResource(
|
||||
new HistoryEntry.Builder()
|
||||
.setParent(domain)
|
||||
.setModificationTime(now.plusDays(1))
|
||||
.build());
|
||||
historyEntry =
|
||||
persistResource(
|
||||
new HistoryEntry.Builder()
|
||||
.setParent(domain)
|
||||
.setModificationTime(now)
|
||||
.setRequestedByRegistrar(false)
|
||||
.setType(HistoryEntry.Type.DOMAIN_CREATE)
|
||||
.setXmlBytes(new byte[0])
|
||||
.build()
|
||||
.toChildHistoryEntity());
|
||||
historyEntry2 =
|
||||
persistResource(
|
||||
new HistoryEntry.Builder()
|
||||
.setParent(domain)
|
||||
.setModificationTime(now.plusDays(1))
|
||||
.setRequestedByRegistrar(false)
|
||||
.setType(HistoryEntry.Type.DOMAIN_CREATE)
|
||||
.setXmlBytes(new byte[0])
|
||||
.build()
|
||||
.toChildHistoryEntity());
|
||||
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
@@ -149,74 +164,38 @@ public class BillingEventTest extends EntityTestCase {
|
||||
.setEventTime(now.plusDays(1))
|
||||
.setBillingTime(now.plusYears(1).plusDays(45))
|
||||
.setRecurringEventKey(recurring.createVKey())));
|
||||
modification = persistResource(commonInit(
|
||||
new BillingEvent.Modification.Builder()
|
||||
.setParent(historyEntry2)
|
||||
.setReason(Reason.CREATE)
|
||||
.setCost(Money.of(USD, 1))
|
||||
.setDescription("Something happened")
|
||||
.setEventTime(now.plusDays(1))
|
||||
.setEventKey(Key.create(oneTime))));
|
||||
modification =
|
||||
ofyTmOrDoNothing(
|
||||
() ->
|
||||
persistResource(
|
||||
commonInit(
|
||||
new BillingEvent.Modification.Builder()
|
||||
.setParent(historyEntry2)
|
||||
.setReason(Reason.CREATE)
|
||||
.setCost(Money.of(USD, 1))
|
||||
.setDescription("Something happened")
|
||||
.setEventTime(now.plusDays(1))
|
||||
.setEventKey(Key.create(oneTime)))));
|
||||
}
|
||||
|
||||
private <E extends BillingEvent, B extends BillingEvent.Builder<E, B>> E commonInit(B builder) {
|
||||
return builder
|
||||
.setClientId("a registrar")
|
||||
.setTargetId("foo.tld")
|
||||
.build();
|
||||
return builder.setClientId("TheRegistrar").setTargetId("foo.tld").build();
|
||||
}
|
||||
|
||||
private void saveNewBillingEvent(BillingEvent billingEvent) {
|
||||
jpaTm().transact(() -> jpaTm().insert(billingEvent));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCloudSqlPersistence_OneTime() {
|
||||
saveRegistrar("a registrar");
|
||||
saveNewBillingEvent(oneTime);
|
||||
|
||||
BillingEvent.OneTime persisted = jpaTm().transact(() -> jpaTm().load(oneTime.createVKey()));
|
||||
// TODO(b/168325240): Remove this fix after VKeyConverter generates symmetric key for
|
||||
// AllocationToken.
|
||||
BillingEvent.OneTime fixed =
|
||||
persisted.asBuilder().setAllocationToken(oneTime.getAllocationToken().get()).build();
|
||||
assertThat(fixed).isEqualTo(oneTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCloudSqlPersistence_Cancellation() {
|
||||
saveRegistrar("a registrar");
|
||||
saveNewBillingEvent(oneTime);
|
||||
saveNewBillingEvent(cancellationOneTime);
|
||||
|
||||
BillingEvent.Cancellation persisted =
|
||||
jpaTm().transact(() -> jpaTm().load(cancellationOneTime.createVKey()));
|
||||
// TODO(b/168537779): Remove this fix after VKey<OneTime> can be reconstructed correctly.
|
||||
BillingEvent.Cancellation fixed =
|
||||
persisted.asBuilder().setOneTimeEventKey(oneTime.createVKey()).build();
|
||||
assertThat(fixed).isEqualTo(cancellationOneTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCloudSqlPersistence_Recurring() {
|
||||
saveRegistrar("a registrar");
|
||||
saveNewBillingEvent(recurring);
|
||||
|
||||
BillingEvent.Recurring persisted = jpaTm().transact(() -> jpaTm().load(recurring.createVKey()));
|
||||
assertThat(persisted).isEqualTo(recurring);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testPersistence() {
|
||||
assertThat(ofy().load().entity(oneTime).now()).isEqualTo(oneTime);
|
||||
assertThat(ofy().load().entity(oneTimeSynthetic).now()).isEqualTo(oneTimeSynthetic);
|
||||
assertThat(ofy().load().entity(recurring).now()).isEqualTo(recurring);
|
||||
assertThat(ofy().load().entity(cancellationOneTime).now()).isEqualTo(cancellationOneTime);
|
||||
assertThat(ofy().load().entity(cancellationRecurring).now()).isEqualTo(cancellationRecurring);
|
||||
assertThat(ofy().load().entity(modification).now()).isEqualTo(modification);
|
||||
assertThat(transactIfJpaTm(() -> tm().load(oneTime))).isEqualTo(oneTime);
|
||||
assertThat(transactIfJpaTm(() -> tm().load(oneTimeSynthetic))).isEqualTo(oneTimeSynthetic);
|
||||
assertThat(transactIfJpaTm(() -> tm().load(recurring))).isEqualTo(recurring);
|
||||
assertThat(transactIfJpaTm(() -> tm().load(cancellationOneTime)))
|
||||
.isEqualTo(cancellationOneTime);
|
||||
assertThat(transactIfJpaTm(() -> tm().load(cancellationRecurring)))
|
||||
.isEqualTo(cancellationRecurring);
|
||||
|
||||
ofyTmOrDoNothing(() -> assertThat(tm().load(modification)).isEqualTo(modification));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyOnly
|
||||
void testParenting() {
|
||||
// Note that these are all tested separately because BillingEvent is an abstract base class that
|
||||
// lacks the @Entity annotation, and thus we cannot call .type(BillingEvent.class)
|
||||
@@ -238,19 +217,14 @@ public class BillingEventTest extends EntityTestCase {
|
||||
.containsExactly(modification);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testCancellationMatching() {
|
||||
Key<?> recurringKey =
|
||||
ofy()
|
||||
.load()
|
||||
.entity(oneTimeSynthetic)
|
||||
.now()
|
||||
.getCancellationMatchingBillingEvent()
|
||||
.getOfyKey();
|
||||
assertThat(ofy().load().key(recurringKey).now()).isEqualTo(recurring);
|
||||
VKey<?> recurringKey =
|
||||
transactIfJpaTm(() -> tm().load(oneTimeSynthetic).getCancellationMatchingBillingEvent());
|
||||
assertThat(transactIfJpaTm(() -> tm().load(recurringKey))).isEqualTo(recurring);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyOnly
|
||||
void testIndexing() throws Exception {
|
||||
verifyIndexing(
|
||||
oneTime,
|
||||
@@ -272,7 +246,7 @@ public class BillingEventTest extends EntityTestCase {
|
||||
verifyIndexing(modification, "clientId", "eventTime");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_syntheticFlagWithoutCreationTime() {
|
||||
IllegalStateException thrown =
|
||||
assertThrows(
|
||||
@@ -288,7 +262,7 @@ public class BillingEventTest extends EntityTestCase {
|
||||
.contains("Synthetic creation time must be set if and only if the SYNTHETIC flag is set.");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_syntheticCreationTimeWithoutFlag() {
|
||||
IllegalStateException thrown =
|
||||
assertThrows(
|
||||
@@ -299,7 +273,7 @@ public class BillingEventTest extends EntityTestCase {
|
||||
.contains("Synthetic creation time must be set if and only if the SYNTHETIC flag is set");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_syntheticFlagWithoutCancellationMatchingKey() {
|
||||
IllegalStateException thrown =
|
||||
assertThrows(
|
||||
@@ -317,7 +291,7 @@ public class BillingEventTest extends EntityTestCase {
|
||||
+ "if and only if the SYNTHETIC flag is set");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_cancellationMatchingKeyWithoutFlag() {
|
||||
IllegalStateException thrown =
|
||||
assertThrows(
|
||||
@@ -334,7 +308,7 @@ public class BillingEventTest extends EntityTestCase {
|
||||
+ "if and only if the SYNTHETIC flag is set");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_cancellation_forGracePeriod_withOneTime() {
|
||||
BillingEvent.Cancellation newCancellation =
|
||||
BillingEvent.Cancellation.forGracePeriod(
|
||||
@@ -343,10 +317,15 @@ public class BillingEventTest extends EntityTestCase {
|
||||
"foo.tld");
|
||||
// Set ID to be the same to ignore for the purposes of comparison.
|
||||
newCancellation = newCancellation.asBuilder().setId(cancellationOneTime.getId()).build();
|
||||
assertThat(newCancellation).isEqualTo(cancellationOneTime);
|
||||
|
||||
// TODO(b/168537779): Remove setRecurringEventKey after symmetric VKey can be reconstructed
|
||||
// correctly.
|
||||
assertThat(newCancellation)
|
||||
.isEqualTo(
|
||||
cancellationOneTime.asBuilder().setOneTimeEventKey(oneTime.createVKey()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testSuccess_cancellation_forGracePeriod_withRecurring() {
|
||||
BillingEvent.Cancellation newCancellation =
|
||||
BillingEvent.Cancellation.forGracePeriod(
|
||||
@@ -354,16 +333,21 @@ public class BillingEventTest extends EntityTestCase {
|
||||
GracePeriodStatus.AUTO_RENEW,
|
||||
domain.getRepoId(),
|
||||
now.plusYears(1).plusDays(45),
|
||||
"a registrar",
|
||||
"TheRegistrar",
|
||||
recurring.createVKey()),
|
||||
historyEntry2,
|
||||
"foo.tld");
|
||||
// Set ID to be the same to ignore for the purposes of comparison.
|
||||
newCancellation = newCancellation.asBuilder().setId(cancellationRecurring.getId()).build();
|
||||
assertThat(newCancellation).isEqualTo(cancellationRecurring);
|
||||
|
||||
// TODO(b/168537779): Remove setRecurringEventKey after symmetric VKey can be reconstructed
|
||||
// correctly.
|
||||
assertThat(newCancellation)
|
||||
.isEqualTo(
|
||||
cancellationRecurring.asBuilder().setRecurringEventKey(recurring.createVKey()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_cancellation_forGracePeriodWithoutBillingEvent() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -380,7 +364,7 @@ public class BillingEventTest extends EntityTestCase {
|
||||
assertThat(thrown).hasMessageThat().contains("grace period without billing event");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_cancellationWithNoBillingEvent() {
|
||||
IllegalStateException thrown =
|
||||
assertThrows(
|
||||
@@ -394,7 +378,7 @@ public class BillingEventTest extends EntityTestCase {
|
||||
assertThat(thrown).hasMessageThat().contains("exactly one billing event");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testFailure_cancellationWithBothBillingEvents() {
|
||||
IllegalStateException thrown =
|
||||
assertThrows(
|
||||
@@ -408,7 +392,7 @@ public class BillingEventTest extends EntityTestCase {
|
||||
assertThat(thrown).hasMessageThat().contains("exactly one billing event");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestOfyAndSql
|
||||
void testDeadCodeThatDeletedScrapCommandsReference() {
|
||||
assertThat(recurring.getParentKey()).isEqualTo(Key.create(historyEntry));
|
||||
new BillingEvent.OneTime.Builder().setParent(Key.create(historyEntry));
|
||||
|
||||
@@ -78,6 +78,7 @@ public class DomainBaseSqlTest {
|
||||
private HostResource host;
|
||||
private ContactResource contact;
|
||||
private ContactResource contact2;
|
||||
private ImmutableSet<GracePeriod> gracePeriods;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
@@ -506,6 +507,20 @@ public class DomainBaseSqlTest {
|
||||
.setServerApproveAutorenewEvent(billEvent.createVKey())
|
||||
.setServerApproveAutorenewPollMessage(autorenewPollMessage.createVKey())
|
||||
.build();
|
||||
gracePeriods =
|
||||
ImmutableSet.of(
|
||||
GracePeriod.create(
|
||||
GracePeriodStatus.ADD,
|
||||
"4-COM",
|
||||
END_OF_TIME,
|
||||
"registrar1",
|
||||
oneTimeBillingEvent.createVKey()),
|
||||
GracePeriod.createForRecurring(
|
||||
GracePeriodStatus.AUTO_RENEW,
|
||||
"4-COM",
|
||||
END_OF_TIME,
|
||||
"registrar1",
|
||||
billEvent.createVKey()));
|
||||
|
||||
jpaTm().insert(contact);
|
||||
jpaTm().insert(contact2);
|
||||
@@ -517,6 +532,7 @@ public class DomainBaseSqlTest {
|
||||
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
|
||||
.setDeletePollMessage(deletePollMessage.createVKey())
|
||||
.setTransferData(transferData)
|
||||
.setGracePeriods(gracePeriods)
|
||||
.build();
|
||||
historyEntry = historyEntry.asBuilder().setDomainContent(domain).build();
|
||||
jpaTm().insert(historyEntry);
|
||||
@@ -553,6 +569,7 @@ public class DomainBaseSqlTest {
|
||||
.isEqualTo(originalTransferData.getServerApproveAutorenewEvent());
|
||||
assertThat(persistedTransferData.getServerApproveAutorenewPollMessage())
|
||||
.isEqualTo(originalTransferData.getServerApproveAutorenewPollMessage());
|
||||
assertThat(persisted.getGracePeriods()).isEqualTo(gracePeriods);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -624,6 +641,21 @@ public class DomainBaseSqlTest {
|
||||
createLegacyVKey(
|
||||
PollMessage.Autorenew.class, autorenewPollMessage.getId()))
|
||||
.build();
|
||||
gracePeriods =
|
||||
ImmutableSet.of(
|
||||
GracePeriod.create(
|
||||
GracePeriodStatus.ADD,
|
||||
"4-COM",
|
||||
END_OF_TIME,
|
||||
"registrar1",
|
||||
createLegacyVKey(
|
||||
BillingEvent.OneTime.class, oneTimeBillingEvent.getId())),
|
||||
GracePeriod.createForRecurring(
|
||||
GracePeriodStatus.AUTO_RENEW,
|
||||
"4-COM",
|
||||
END_OF_TIME,
|
||||
"registrar1",
|
||||
createLegacyVKey(BillingEvent.Recurring.class, billEvent.getId())));
|
||||
|
||||
jpaTm().insert(contact);
|
||||
jpaTm().insert(contact2);
|
||||
@@ -639,6 +671,7 @@ public class DomainBaseSqlTest {
|
||||
.setDeletePollMessage(
|
||||
createLegacyVKey(PollMessage.OneTime.class, deletePollMessage.getId()))
|
||||
.setTransferData(transferData)
|
||||
.setGracePeriods(gracePeriods)
|
||||
.build();
|
||||
historyEntry = historyEntry.asBuilder().setDomainContent(domain).build();
|
||||
jpaTm().insert(historyEntry);
|
||||
@@ -675,6 +708,7 @@ public class DomainBaseSqlTest {
|
||||
.isEqualTo(originalTransferData.getServerApproveAutorenewEvent());
|
||||
assertThat(persistedTransferData.getServerApproveAutorenewPollMessage())
|
||||
.isEqualTo(originalTransferData.getServerApproveAutorenewPollMessage());
|
||||
assertThat(domain.getGracePeriods()).isEqualTo(gracePeriods);
|
||||
}
|
||||
|
||||
private <T> VKey<T> createLegacyVKey(Class<T> clazz, long id) {
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.google.common.collect.Ordering;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.EntityTestCase;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
@@ -830,4 +831,63 @@ public class DomainBaseTest extends EntityTestCase {
|
||||
assertThat(getOnlyElement(clone.getGracePeriods()).getType())
|
||||
.isEqualTo(GracePeriodStatus.TRANSFER);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHistoryIdRestoration() {
|
||||
// Verify that history ids for billing events are restored during load from datastore. History
|
||||
// ids are not used by business code or persisted in datastore, but only to reconstruct
|
||||
// objectify keys when loading from SQL.
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
domain =
|
||||
persistResource(
|
||||
domain
|
||||
.asBuilder()
|
||||
.setRegistrationExpirationTime(now.plusYears(1))
|
||||
.setGracePeriods(
|
||||
ImmutableSet.of(
|
||||
GracePeriod.createForRecurring(
|
||||
GracePeriodStatus.AUTO_RENEW,
|
||||
domain.getRepoId(),
|
||||
now.plusDays(1),
|
||||
"NewRegistrar",
|
||||
recurringBillKey),
|
||||
GracePeriod.create(
|
||||
GracePeriodStatus.RENEW,
|
||||
domain.getRepoId(),
|
||||
now.plusDays(1),
|
||||
"NewRegistrar",
|
||||
oneTimeBillKey)))
|
||||
.build());
|
||||
ImmutableSet<BillEventInfo> historyIds =
|
||||
domain.getGracePeriods().stream()
|
||||
.map(
|
||||
gp ->
|
||||
new BillEventInfo(
|
||||
gp.getRecurringBillingEvent(), gp.billingEventRecurringHistoryId,
|
||||
gp.getOneTimeBillingEvent(), gp.billingEventOneTimeHistoryId))
|
||||
.collect(toImmutableSet());
|
||||
assertThat(historyIds)
|
||||
.isEqualTo(
|
||||
ImmutableSet.of(
|
||||
new BillEventInfo(null, null, oneTimeBillKey, historyEntryKey.getId()),
|
||||
new BillEventInfo(recurringBillKey, historyEntryKey.getId(), null, null)));
|
||||
}
|
||||
|
||||
static class BillEventInfo extends ImmutableObject {
|
||||
VKey<BillingEvent.Recurring> billingEventRecurring;
|
||||
Long billingEventRecurringHistoryId;
|
||||
VKey<BillingEvent.OneTime> billingEventOneTime;
|
||||
Long billingEventOneTimeHistoryId;
|
||||
|
||||
BillEventInfo(
|
||||
VKey<BillingEvent.Recurring> billingEventRecurring,
|
||||
Long billingEventRecurringHistoryId,
|
||||
VKey<BillingEvent.OneTime> billingEventOneTime,
|
||||
Long billingEventOneTimeHistoryId) {
|
||||
this.billingEventRecurring = billingEventRecurring;
|
||||
this.billingEventRecurringHistoryId = billingEventRecurringHistoryId;
|
||||
this.billingEventOneTime = billingEventOneTime;
|
||||
this.billingEventOneTimeHistoryId = billingEventOneTimeHistoryId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ public class GracePeriodTest {
|
||||
|
||||
private final DateTime now = DateTime.now(UTC);
|
||||
private BillingEvent.OneTime onetime;
|
||||
private VKey<BillingEvent.Recurring> recurringKey;
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
@@ -59,6 +60,14 @@ public class GracePeriodTest {
|
||||
.setPeriodYears(1)
|
||||
.setTargetId("foo.google")
|
||||
.build();
|
||||
recurringKey =
|
||||
VKey.create(
|
||||
Recurring.class,
|
||||
12345,
|
||||
Key.create(
|
||||
Key.create(Key.create(DomainBase.class, "1-TEST"), HistoryEntry.class, 343L),
|
||||
Recurring.class,
|
||||
12345));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -71,6 +80,24 @@ public class GracePeriodTest {
|
||||
assertThat(gracePeriod.getClientId()).isEqualTo("TheRegistrar");
|
||||
assertThat(gracePeriod.getExpirationTime()).isEqualTo(now.plusDays(1));
|
||||
assertThat(gracePeriod.hasBillingEvent()).isTrue();
|
||||
assertThat(gracePeriod.billingEventOneTimeHistoryId).isEqualTo(12345L);
|
||||
assertThat(gracePeriod.billingEventRecurringHistoryId).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_forRecurringEvent() {
|
||||
GracePeriod gracePeriod =
|
||||
GracePeriod.createForRecurring(
|
||||
GracePeriodStatus.AUTO_RENEW, "1-TEST", now.plusDays(1), "TheRegistrar", recurringKey);
|
||||
assertThat(gracePeriod.getType()).isEqualTo(GracePeriodStatus.AUTO_RENEW);
|
||||
assertThat(gracePeriod.getDomainRepoId()).isEqualTo("1-TEST");
|
||||
assertThat(gracePeriod.getOneTimeBillingEvent()).isNull();
|
||||
assertThat(gracePeriod.getRecurringBillingEvent()).isEqualTo(recurringKey);
|
||||
assertThat(gracePeriod.getClientId()).isEqualTo("TheRegistrar");
|
||||
assertThat(gracePeriod.getExpirationTime()).isEqualTo(now.plusDays(1));
|
||||
assertThat(gracePeriod.hasBillingEvent()).isTrue();
|
||||
assertThat(gracePeriod.billingEventOneTimeHistoryId).isNull();
|
||||
assertThat(gracePeriod.billingEventRecurringHistoryId).isEqualTo(343L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -98,11 +125,6 @@ public class GracePeriodTest {
|
||||
|
||||
@Test
|
||||
void testFailure_createForRecurring_notAutoRenew() {
|
||||
Key<Recurring> recurringKey =
|
||||
Key.create(
|
||||
Key.create(Key.create(DomainBase.class, "1-TEST"), HistoryEntry.class, 343L),
|
||||
Recurring.class,
|
||||
12345);
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
@@ -112,7 +134,7 @@ public class GracePeriodTest {
|
||||
"1-TEST",
|
||||
now.plusDays(1),
|
||||
"TheRegistrar",
|
||||
VKey.create(Recurring.class, 12345, recurringKey)));
|
||||
recurringKey));
|
||||
assertThat(thrown).hasMessageThat().contains("autorenew");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainContent;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
@@ -141,6 +142,7 @@ public class DomainHistoryTest extends EntityTestCase {
|
||||
newDomainBase("example.tld", "domainRepoId", contact)
|
||||
.asBuilder()
|
||||
.setNameservers(host.createVKey())
|
||||
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2})))
|
||||
.build();
|
||||
jpaTm().transact(() -> jpaTm().insert(domain));
|
||||
return domain;
|
||||
|
||||
@@ -116,7 +116,8 @@ public class LegacyHistoryObjectTest extends EntityTestCase {
|
||||
// The objects will be mostly the same, but the DomainHistory object has a couple extra fields
|
||||
assertAboutImmutableObjects()
|
||||
.that(legacyHistoryEntry)
|
||||
.isEqualExceptFields(fromObjectify, "domainContent", "domainRepoId", "nsHosts");
|
||||
.isEqualExceptFields(
|
||||
fromObjectify, "domainContent", "domainRepoId", "nsHosts", "dsDataHistories");
|
||||
assertThat(fromObjectify instanceof DomainHistory).isTrue();
|
||||
DomainHistory legacyDomainHistory = (DomainHistory) fromObjectify;
|
||||
|
||||
@@ -129,7 +130,12 @@ public class LegacyHistoryObjectTest extends EntityTestCase {
|
||||
.that(legacyDomainHistory)
|
||||
.isEqualExceptFields(
|
||||
// NB: period, transaction records, and other client ID are added in #794
|
||||
legacyHistoryFromSql, "period", "domainTransactionRecords", "otherClientId", "nsHosts");
|
||||
legacyHistoryFromSql,
|
||||
"period",
|
||||
"domainTransactionRecords",
|
||||
"otherClientId",
|
||||
"nsHosts",
|
||||
"dsDataHistories");
|
||||
assertThat(nullToEmpty(legacyDomainHistory.getNsHosts()))
|
||||
.isEqualTo(nullToEmpty(legacyHistoryFromSql.getNsHosts()));
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import google.registry.model.EntityTestCase;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.TestTemplate;
|
||||
|
||||
/** Unit tests for {@link RdeRevision}. */
|
||||
@DualDatabaseTest
|
||||
@@ -40,20 +40,20 @@ public class RdeRevisionTest extends EntityTestCase {
|
||||
fakeClock.setTo(DateTime.parse("1984-12-18TZ"));
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void testGetNextRevision_objectDoesntExist_returnsZero() {
|
||||
tm().transact(
|
||||
() -> assertThat(getNextRevision("torment", fakeClock.nowUtc(), FULL)).isEqualTo(0));
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void testGetNextRevision_objectExistsAtZero_returnsOne() {
|
||||
save("sorrow", fakeClock.nowUtc(), FULL, 0);
|
||||
tm().transact(
|
||||
() -> assertThat(getNextRevision("sorrow", fakeClock.nowUtc(), FULL)).isEqualTo(1));
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void testSaveRevision_objectDoesntExist_newRevisionIsZero_nextRevIsOne() {
|
||||
tm().transact(() -> saveRevision("despondency", fakeClock.nowUtc(), FULL, 0));
|
||||
tm().transact(
|
||||
@@ -61,7 +61,7 @@ public class RdeRevisionTest extends EntityTestCase {
|
||||
assertThat(getNextRevision("despondency", fakeClock.nowUtc(), FULL)).isEqualTo(1));
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void testSaveRevision_objectDoesntExist_newRevisionIsOne_throwsVe() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -74,7 +74,7 @@ public class RdeRevisionTest extends EntityTestCase {
|
||||
+ "when trying to save new revision 1");
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void testSaveRevision_objectExistsAtZero_newRevisionIsZero_throwsVe() {
|
||||
save("melancholy", fakeClock.nowUtc(), FULL, 0);
|
||||
IllegalArgumentException thrown =
|
||||
@@ -84,7 +84,7 @@ public class RdeRevisionTest extends EntityTestCase {
|
||||
assertThat(thrown).hasMessageThat().contains("object already created");
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void testSaveRevision_objectExistsAtZero_newRevisionIsOne_nextRevIsTwo() {
|
||||
DateTime startOfDay = fakeClock.nowUtc().withTimeAtStartOfDay();
|
||||
save("melancholy", startOfDay, FULL, 0);
|
||||
@@ -93,7 +93,7 @@ public class RdeRevisionTest extends EntityTestCase {
|
||||
tm().transact(() -> assertThat(getNextRevision("melancholy", startOfDay, FULL)).isEqualTo(2));
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void testSaveRevision_objectExistsAtZero_newRevisionIsTwo_throwsVe() {
|
||||
save("melancholy", fakeClock.nowUtc(), FULL, 0);
|
||||
IllegalArgumentException thrown =
|
||||
@@ -105,7 +105,7 @@ public class RdeRevisionTest extends EntityTestCase {
|
||||
.contains("RDE revision object should be at revision 1 but was");
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void testSaveRevision_negativeRevision_throwsIae() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -114,7 +114,7 @@ public class RdeRevisionTest extends EntityTestCase {
|
||||
assertThat(thrown).hasMessageThat().contains("Negative revision");
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void testSaveRevision_callerNotInTransaction_throwsIse() {
|
||||
IllegalStateException thrown =
|
||||
assertThrows(
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.server;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
|
||||
import google.registry.persistence.transaction.JpaTestRules;
|
||||
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageExtension;
|
||||
import google.registry.testing.DatastoreEntityExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Tests for {@link google.registry.model.server.KmsSecretRevisionSqlDao}. */
|
||||
public class KmsSecretRevisionSqlDaoTest {
|
||||
|
||||
private final FakeClock fakeClock = new FakeClock();
|
||||
|
||||
@RegisterExtension
|
||||
@Order(value = 1)
|
||||
DatastoreEntityExtension datastoreEntityExtension = new DatastoreEntityExtension();
|
||||
|
||||
@RegisterExtension
|
||||
JpaIntegrationWithCoverageExtension jpa =
|
||||
new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
|
||||
|
||||
@Test
|
||||
void testSaveAndRetrieve() {
|
||||
KmsSecretRevision revision = createRevision();
|
||||
jpaTm().transact(() -> KmsSecretRevisionSqlDao.save(revision));
|
||||
Optional<KmsSecretRevision> fromSql =
|
||||
jpaTm().transact(() -> KmsSecretRevisionSqlDao.getLatestRevision("secretName"));
|
||||
assertThat(fromSql.isPresent()).isTrue();
|
||||
assertAboutImmutableObjects().that(revision).isEqualExceptFields(fromSql.get(), "creationTime");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleRevisions() {
|
||||
KmsSecretRevision revision = createRevision();
|
||||
jpaTm().transact(() -> KmsSecretRevisionSqlDao.save(revision));
|
||||
|
||||
KmsSecretRevision secondRevision = createRevision();
|
||||
secondRevision.encryptedValue = "someOtherValue";
|
||||
jpaTm().transact(() -> KmsSecretRevisionSqlDao.save(secondRevision));
|
||||
|
||||
Optional<KmsSecretRevision> fromSql =
|
||||
jpaTm().transact(() -> KmsSecretRevisionSqlDao.getLatestRevision("secretName"));
|
||||
assertThat(fromSql.isPresent()).isTrue();
|
||||
assertThat(fromSql.get().getEncryptedValue()).isEqualTo("someOtherValue");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNonexistent() {
|
||||
KmsSecretRevision revision = createRevision();
|
||||
jpaTm().transact(() -> KmsSecretRevisionSqlDao.save(revision));
|
||||
assertThat(
|
||||
jpaTm()
|
||||
.transact(() -> KmsSecretRevisionSqlDao.getLatestRevision("someOtherSecretName"))
|
||||
.isPresent())
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
private KmsSecretRevision createRevision() {
|
||||
return new KmsSecretRevision.Builder()
|
||||
.setEncryptedValue("encrypted")
|
||||
.setKmsCryptoKeyVersionName("version")
|
||||
.setParent("secretName")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.smd;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.persistence.transaction.JpaTestRules;
|
||||
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageExtension;
|
||||
import google.registry.testing.DatastoreEntityExtension;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.FakeClock;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
@DualDatabaseTest
|
||||
public class SignedMarkRevocationListDaoTest {
|
||||
|
||||
private final FakeClock fakeClock = new FakeClock();
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationWithCoverageExtension jpa =
|
||||
new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
|
||||
|
||||
@RegisterExtension
|
||||
@Order(value = 1)
|
||||
final DatastoreEntityExtension datastoreEntityExtension = new DatastoreEntityExtension();
|
||||
|
||||
@Test
|
||||
void testSave_success() {
|
||||
SignedMarkRevocationList list =
|
||||
SignedMarkRevocationList.create(
|
||||
fakeClock.nowUtc(), ImmutableMap.of("mark", fakeClock.nowUtc().minusHours(1)));
|
||||
SignedMarkRevocationListDao.trySave(list);
|
||||
SignedMarkRevocationList fromDb = SignedMarkRevocationListDao.getLatestRevision().get();
|
||||
assertAboutImmutableObjects().that(fromDb).isEqualExceptFields(list);
|
||||
}
|
||||
|
||||
@Test
|
||||
void trySave_failureIsSwallowed() {
|
||||
SignedMarkRevocationList list =
|
||||
SignedMarkRevocationList.create(
|
||||
fakeClock.nowUtc(), ImmutableMap.of("mark", fakeClock.nowUtc().minusHours(1)));
|
||||
SignedMarkRevocationListDao.trySave(list);
|
||||
SignedMarkRevocationList fromDb = SignedMarkRevocationListDao.getLatestRevision().get();
|
||||
assertAboutImmutableObjects().that(fromDb).isEqualExceptFields(list);
|
||||
|
||||
// This should throw an exception, which is swallowed and nothing changed
|
||||
SignedMarkRevocationListDao.trySave(list);
|
||||
SignedMarkRevocationList secondFromDb = SignedMarkRevocationListDao.getLatestRevision().get();
|
||||
assertAboutImmutableObjects().that(secondFromDb).isEqualExceptFields(fromDb);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRetrieval_notPresent() {
|
||||
assertThat(SignedMarkRevocationListDao.getLatestRevision().isPresent()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSaveAndRetrieval_emptyList() {
|
||||
SignedMarkRevocationList list =
|
||||
SignedMarkRevocationList.create(fakeClock.nowUtc(), ImmutableMap.of());
|
||||
SignedMarkRevocationListDao.trySave(list);
|
||||
SignedMarkRevocationList fromDb = SignedMarkRevocationListDao.getLatestRevision().get();
|
||||
assertAboutImmutableObjects().that(fromDb).isEqualExceptFields(list);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSave_multipleVersions() {
|
||||
SignedMarkRevocationList list =
|
||||
SignedMarkRevocationList.create(
|
||||
fakeClock.nowUtc(), ImmutableMap.of("mark", fakeClock.nowUtc().minusHours(1)));
|
||||
SignedMarkRevocationListDao.trySave(list);
|
||||
assertThat(
|
||||
SignedMarkRevocationListDao.getLatestRevision()
|
||||
.get()
|
||||
.isSmdRevoked("mark", fakeClock.nowUtc()))
|
||||
.isTrue();
|
||||
|
||||
// Now remove the revocation
|
||||
SignedMarkRevocationList secondList =
|
||||
SignedMarkRevocationList.create(fakeClock.nowUtc(), ImmutableMap.of());
|
||||
SignedMarkRevocationListDao.trySave(secondList);
|
||||
assertThat(
|
||||
SignedMarkRevocationListDao.getLatestRevision()
|
||||
.get()
|
||||
.isSmdRevoked("mark", fakeClock.nowUtc()))
|
||||
.isFalse();
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.model.smd;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.model.smd.SignedMarkRevocationList.SHARD_SIZE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
@@ -72,10 +73,11 @@ public class SignedMarkRevocationListTest {
|
||||
revokes.put(Integer.toString(i), clock.nowUtc());
|
||||
}
|
||||
// Save it with sharding, and make sure that reloading it works.
|
||||
SignedMarkRevocationList unsharded = SignedMarkRevocationList
|
||||
.create(clock.nowUtc(), revokes.build())
|
||||
.save();
|
||||
assertThat(SignedMarkRevocationList.get()).isEqualTo(unsharded);
|
||||
SignedMarkRevocationList unsharded =
|
||||
SignedMarkRevocationList.create(clock.nowUtc(), revokes.build()).save();
|
||||
assertAboutImmutableObjects()
|
||||
.that(SignedMarkRevocationList.get())
|
||||
.isEqualExceptFields(unsharded, "revisionId");
|
||||
assertThat(ofy().load().type(SignedMarkRevocationList.class).count()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@@ -91,7 +93,9 @@ public class SignedMarkRevocationListTest {
|
||||
SignedMarkRevocationList unsharded = SignedMarkRevocationList
|
||||
.create(clock.nowUtc(), revokes.build())
|
||||
.save();
|
||||
assertThat(SignedMarkRevocationList.get()).isEqualTo(unsharded);
|
||||
assertAboutImmutableObjects()
|
||||
.that(SignedMarkRevocationList.get())
|
||||
.isEqualExceptFields(unsharded, "revisionId");
|
||||
assertThat(ofy().load().type(SignedMarkRevocationList.class).count()).isEqualTo(4);
|
||||
}
|
||||
|
||||
|
||||
+44
-26
@@ -33,6 +33,8 @@ import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import google.registry.testing.TestOfyOnly;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
@@ -40,7 +42,6 @@ import java.util.stream.Stream;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.TestTemplate;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/**
|
||||
@@ -78,28 +79,28 @@ public class TransactionManagerTest {
|
||||
fakeClock.advanceOneMilli();
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void inTransaction_returnsCorrespondingResult() {
|
||||
assertThat(tm().inTransaction()).isFalse();
|
||||
tm().transact(() -> assertThat(tm().inTransaction()).isTrue());
|
||||
assertThat(tm().inTransaction()).isFalse();
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void assertInTransaction_throwsExceptionWhenNotInTransaction() {
|
||||
assertThrows(IllegalStateException.class, () -> tm().assertInTransaction());
|
||||
tm().transact(() -> tm().assertInTransaction());
|
||||
assertThrows(IllegalStateException.class, () -> tm().assertInTransaction());
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void getTransactionTime_throwsExceptionWhenNotInTransaction() {
|
||||
assertThrows(IllegalStateException.class, () -> tm().getTransactionTime());
|
||||
tm().transact(() -> assertThat(tm().getTransactionTime()).isEqualTo(fakeClock.nowUtc()));
|
||||
assertThrows(IllegalStateException.class, () -> tm().getTransactionTime());
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void transact_hasNoEffectWithPartialSuccess() {
|
||||
assertEntityNotExist(theEntity);
|
||||
assertThrows(
|
||||
@@ -113,21 +114,21 @@ public class TransactionManagerTest {
|
||||
assertEntityNotExist(theEntity);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void transact_reusesExistingTransaction() {
|
||||
assertEntityNotExist(theEntity);
|
||||
tm().transact(() -> tm().transact(() -> tm().insert(theEntity)));
|
||||
assertEntityExists(theEntity);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void transactNew_succeeds() {
|
||||
assertEntityNotExist(theEntity);
|
||||
tm().transactNew(() -> tm().insert(theEntity));
|
||||
assertEntityExists(theEntity);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void transactNewReadOnly_succeeds() {
|
||||
assertEntityNotExist(theEntity);
|
||||
tm().transact(() -> tm().insert(theEntity));
|
||||
@@ -136,7 +137,7 @@ public class TransactionManagerTest {
|
||||
assertThat(persisted).isEqualTo(theEntity);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void transactNewReadOnly_throwsWhenWritingEntity() {
|
||||
assertEntityNotExist(theEntity);
|
||||
assertThrows(
|
||||
@@ -144,7 +145,7 @@ public class TransactionManagerTest {
|
||||
assertEntityNotExist(theEntity);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void saveNew_succeeds() {
|
||||
assertEntityNotExist(theEntity);
|
||||
tm().transact(() -> tm().insert(theEntity));
|
||||
@@ -152,14 +153,14 @@ public class TransactionManagerTest {
|
||||
assertThat(tm().transact(() -> tm().load(theEntity.key()))).isEqualTo(theEntity);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void saveAllNew_succeeds() {
|
||||
assertAllEntitiesNotExist(moreEntities);
|
||||
tm().transact(() -> tm().insertAll(moreEntities));
|
||||
assertAllEntitiesExist(moreEntities);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void saveNewOrUpdate_persistsNewEntity() {
|
||||
assertEntityNotExist(theEntity);
|
||||
tm().transact(() -> tm().put(theEntity));
|
||||
@@ -167,7 +168,7 @@ public class TransactionManagerTest {
|
||||
assertThat(tm().transact(() -> tm().load(theEntity.key()))).isEqualTo(theEntity);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void saveNewOrUpdate_updatesExistingEntity() {
|
||||
tm().transact(() -> tm().insert(theEntity));
|
||||
TestEntity persisted = tm().transact(() -> tm().load(theEntity.key()));
|
||||
@@ -179,14 +180,14 @@ public class TransactionManagerTest {
|
||||
assertThat(persisted.data).isEqualTo("bar");
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void saveNewOrUpdateAll_succeeds() {
|
||||
assertAllEntitiesNotExist(moreEntities);
|
||||
tm().transact(() -> tm().putAll(moreEntities));
|
||||
assertAllEntitiesExist(moreEntities);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void update_succeeds() {
|
||||
tm().transact(() -> tm().insert(theEntity));
|
||||
TestEntity persisted =
|
||||
@@ -202,7 +203,7 @@ public class TransactionManagerTest {
|
||||
assertThat(persisted.data).isEqualTo("bar");
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void load_succeeds() {
|
||||
assertEntityNotExist(theEntity);
|
||||
tm().transact(() -> tm().insert(theEntity));
|
||||
@@ -211,14 +212,14 @@ public class TransactionManagerTest {
|
||||
assertThat(persisted.data).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void load_throwsOnMissingElement() {
|
||||
assertEntityNotExist(theEntity);
|
||||
assertThrows(
|
||||
NoSuchElementException.class, () -> tm().transact(() -> tm().load(theEntity.key())));
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void maybeLoad_succeeds() {
|
||||
assertEntityNotExist(theEntity);
|
||||
tm().transact(() -> tm().insert(theEntity));
|
||||
@@ -227,13 +228,13 @@ public class TransactionManagerTest {
|
||||
assertThat(persisted.data).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void maybeLoad_nonExistentObject() {
|
||||
assertEntityNotExist(theEntity);
|
||||
assertThat(tm().transact(() -> tm().maybeLoad(theEntity.key())).isPresent()).isFalse();
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void delete_succeeds() {
|
||||
tm().transact(() -> tm().insert(theEntity));
|
||||
assertEntityExists(theEntity);
|
||||
@@ -242,14 +243,14 @@ public class TransactionManagerTest {
|
||||
assertEntityNotExist(theEntity);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void delete_doNothingWhenEntityNotExist() {
|
||||
assertEntityNotExist(theEntity);
|
||||
tm().transact(() -> tm().delete(theEntity.key()));
|
||||
assertEntityNotExist(theEntity);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void delete_succeedsForEntitySet() {
|
||||
assertAllEntitiesNotExist(moreEntities);
|
||||
tm().transact(() -> tm().insertAll(moreEntities));
|
||||
@@ -261,7 +262,7 @@ public class TransactionManagerTest {
|
||||
assertAllEntitiesNotExist(moreEntities);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void delete_ignoreNonExistentEntity() {
|
||||
assertAllEntitiesNotExist(moreEntities);
|
||||
tm().transact(() -> tm().insertAll(moreEntities));
|
||||
@@ -276,7 +277,16 @@ public class TransactionManagerTest {
|
||||
assertAllEntitiesNotExist(moreEntities);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void delete_deletesTheGivenEntity() {
|
||||
tm().transact(() -> tm().insert(theEntity));
|
||||
assertEntityExists(theEntity);
|
||||
fakeClock.advanceOneMilli();
|
||||
tm().transact(() -> tm().delete(theEntity));
|
||||
assertEntityNotExist(theEntity);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void load_multi() {
|
||||
assertAllEntitiesNotExist(moreEntities);
|
||||
tm().transact(() -> tm().insertAll(moreEntities));
|
||||
@@ -286,7 +296,7 @@ public class TransactionManagerTest {
|
||||
.isEqualTo(Maps.uniqueIndex(moreEntities, TestEntity::key));
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void load_multiWithDuplicateKeys() {
|
||||
assertAllEntitiesNotExist(moreEntities);
|
||||
tm().transact(() -> tm().insertAll(moreEntities));
|
||||
@@ -298,7 +308,7 @@ public class TransactionManagerTest {
|
||||
.isEqualTo(Maps.uniqueIndex(moreEntities, TestEntity::key));
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@TestOfyAndSql
|
||||
void load_multiMissingKeys() {
|
||||
assertAllEntitiesNotExist(moreEntities);
|
||||
tm().transact(() -> tm().insertAll(moreEntities));
|
||||
@@ -310,6 +320,14 @@ public class TransactionManagerTest {
|
||||
.isEqualTo(Maps.uniqueIndex(moreEntities, TestEntity::key));
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void loadAllForOfyTm_throwsExceptionInTransaction() {
|
||||
assertAllEntitiesNotExist(moreEntities);
|
||||
tm().transact(() -> tm().insertAll(moreEntities));
|
||||
assertThrows(
|
||||
IllegalArgumentException.class, () -> tm().transact(() -> tm().loadAll(TestEntity.class)));
|
||||
}
|
||||
|
||||
private static void assertEntityExists(TestEntity entity) {
|
||||
assertThat(tm().transact(() -> tm().exists(entity))).isTrue();
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ import google.registry.model.registry.RegistryLockDaoTest;
|
||||
import google.registry.model.registry.RegistryTest;
|
||||
import google.registry.model.registry.label.ReservedListSqlDaoTest;
|
||||
import google.registry.model.reporting.Spec11ThreatMatchTest;
|
||||
import google.registry.model.server.KmsSecretRevisionSqlDaoTest;
|
||||
import google.registry.model.smd.SignedMarkRevocationListDaoTest;
|
||||
import google.registry.model.tmch.ClaimsListDaoTest;
|
||||
import google.registry.persistence.transaction.JpaEntityCoverageExtension;
|
||||
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageExtension;
|
||||
@@ -84,6 +86,7 @@ import org.junit.runner.RunWith;
|
||||
DomainBaseSqlTest.class,
|
||||
DomainHistoryTest.class,
|
||||
HostHistoryTest.class,
|
||||
KmsSecretRevisionSqlDaoTest.class,
|
||||
LockDaoTest.class,
|
||||
PollMessageTest.class,
|
||||
PremiumListDaoTest.class,
|
||||
@@ -92,6 +95,7 @@ import org.junit.runner.RunWith;
|
||||
RegistryTest.class,
|
||||
ReservedListSqlDaoTest.class,
|
||||
RegistryLockDaoTest.class,
|
||||
SignedMarkRevocationListDaoTest.class,
|
||||
Spec11ThreatMatchTest.class,
|
||||
// AfterSuiteTest must be the last entry. See class javadoc for details.
|
||||
AfterSuiteTest.class
|
||||
|
||||
@@ -61,7 +61,7 @@ public enum Fixture {
|
||||
createTlds("xn--q9jyb4c", "example");
|
||||
|
||||
// Used for OT&E TLDs
|
||||
persistPremiumList("default_sandbox_list");
|
||||
persistPremiumList("default_sandbox_list", "sandbox,USD 1000");
|
||||
|
||||
try {
|
||||
OteStatsTestHelper.setupCompleteOte("otefinished");
|
||||
|
||||
@@ -49,7 +49,7 @@ abstract class AbstractEppResourceSubject<
|
||||
this.actual = subject;
|
||||
}
|
||||
|
||||
private List<HistoryEntry> getHistoryEntries() {
|
||||
private List<? extends HistoryEntry> getHistoryEntries() {
|
||||
return DatastoreHelper.getHistoryEntries(actual);
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ abstract class AbstractEppResourceSubject<
|
||||
// TODO(weiminyu): Remove after next Truth update
|
||||
@SuppressWarnings("UnnecessaryParentheses")
|
||||
public Which<HistoryEntrySubject> hasHistoryEntryAtIndex(int index) {
|
||||
List<HistoryEntry> historyEntries = getHistoryEntries();
|
||||
List<? extends HistoryEntry> historyEntries = getHistoryEntries();
|
||||
check("getHistoryEntries().size()").that(historyEntries.size()).isAtLeast(index + 1);
|
||||
return new Which<>(
|
||||
check("getHistoryEntries(%s)", index)
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.testing;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.io.Files.asCharSink;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.ofyOrJpaTm;
|
||||
import static google.registry.testing.DatastoreHelper.persistSimpleResources;
|
||||
import static google.registry.testing.DualDatabaseTestInvocationContextProvider.injectTmForDualDatabaseTest;
|
||||
import static google.registry.testing.DualDatabaseTestInvocationContextProvider.restoreTmAfterDualDatabaseTest;
|
||||
@@ -384,6 +385,17 @@ public final class AppEngineExtension implements BeforeEachCallback, AfterEachCa
|
||||
if (isWithDatastoreAndCloudSql()) {
|
||||
injectTmForDualDatabaseTest(context);
|
||||
}
|
||||
ofyOrJpaTm(
|
||||
() -> {
|
||||
if (withDatastore && !withoutCannedData) {
|
||||
loadInitialData();
|
||||
}
|
||||
},
|
||||
() -> {
|
||||
if (withCloudSql && !withJpaUnitTest && !withoutCannedData) {
|
||||
loadInitialData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -458,9 +470,6 @@ public final class AppEngineExtension implements BeforeEachCallback, AfterEachCa
|
||||
ObjectifyService.initOfy();
|
||||
// Reset id allocation in ObjectifyService so that ids are deterministic in tests.
|
||||
ObjectifyService.resetNextTestId();
|
||||
if (!withoutCannedData) {
|
||||
loadInitialData();
|
||||
}
|
||||
this.ofyTestEntities.forEach(AppEngineExtension::register);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.base.Suppliers.memoize;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.collect.Iterables.toArray;
|
||||
import static com.google.common.collect.MoreCollectors.onlyElement;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
@@ -33,6 +34,9 @@ import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.model.registry.Registry.TldState.GENERAL_AVAILABILITY;
|
||||
import static google.registry.model.registry.label.PremiumListUtils.parentPremiumListEntriesOnRevision;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.ofyOrJpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.ofyTmOrDoNothing;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
|
||||
import static google.registry.util.CollectionUtils.difference;
|
||||
import static google.registry.util.CollectionUtils.union;
|
||||
@@ -44,6 +48,7 @@ import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
import static google.registry.util.ResourceUtils.readResourceUtf8;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Splitter;
|
||||
@@ -60,22 +65,27 @@ import google.registry.dns.writer.VoidDnsWriter;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.EppResource.ForeignKeyedEppResource;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.contact.ContactAuthInfo;
|
||||
import google.registry.model.contact.ContactBase;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.DesignatedContact.Type;
|
||||
import google.registry.model.domain.DomainAuthInfo;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainContent;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.host.HostBase;
|
||||
import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.index.EppResourceIndex;
|
||||
import google.registry.model.index.EppResourceIndexBucket;
|
||||
@@ -101,10 +111,14 @@ import google.registry.persistence.VKey;
|
||||
import google.registry.tmch.LordnTaskUtils;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeComparator;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
/** Static utils for setting up test resources. */
|
||||
public class DatastoreHelper {
|
||||
@@ -325,18 +339,40 @@ public class DatastoreHelper {
|
||||
* the requirement to have monotonically increasing timestamps.
|
||||
*/
|
||||
public static PremiumList persistPremiumList(String listName, String... lines) {
|
||||
PremiumList premiumList = new PremiumList.Builder().setName(listName).build();
|
||||
ImmutableMap<String, PremiumListEntry> entries = premiumList.parse(asList(lines));
|
||||
checkState(lines.length != 0, "Must provide at least one premium entry");
|
||||
PremiumList partialPremiumList = new PremiumList.Builder().setName(listName).build();
|
||||
ImmutableMap<String, PremiumListEntry> entries = partialPremiumList.parse(asList(lines));
|
||||
CurrencyUnit currencyUnit =
|
||||
entries.entrySet().iterator().next().getValue().getValue().getCurrencyUnit();
|
||||
PremiumList premiumList =
|
||||
partialPremiumList
|
||||
.asBuilder()
|
||||
.setCreationTime(DateTime.now(DateTimeZone.UTC))
|
||||
.setCurrency(currencyUnit)
|
||||
.setLabelsToPrices(
|
||||
entries.entrySet().stream()
|
||||
.collect(
|
||||
toImmutableMap(
|
||||
Map.Entry::getKey, entry -> entry.getValue().getValue().getAmount())))
|
||||
.build();
|
||||
PremiumListRevision revision = PremiumListRevision.create(premiumList, entries.keySet());
|
||||
ofy()
|
||||
.saveWithoutBackup()
|
||||
.entities(premiumList.asBuilder().setRevision(Key.create(revision)).build(), revision)
|
||||
.now();
|
||||
ofy()
|
||||
.saveWithoutBackup()
|
||||
.entities(parentPremiumListEntriesOnRevision(entries.values(), Key.create(revision)))
|
||||
.now();
|
||||
return ofy().load().entity(premiumList).now();
|
||||
|
||||
ofyOrJpaTm(
|
||||
() -> {
|
||||
tm().putAllWithoutBackup(
|
||||
ImmutableList.of(
|
||||
premiumList.asBuilder().setRevision(Key.create(revision)).build(), revision));
|
||||
tm().putAllWithoutBackup(
|
||||
parentPremiumListEntriesOnRevision(entries.values(), Key.create(revision)));
|
||||
},
|
||||
() -> tm().transact(() -> tm().insert(premiumList)));
|
||||
// The above premiumList is in the session cache and it is different from the corresponding
|
||||
// entity stored in Datastore because it has some @Ignore fields set dedicated for SQL. This
|
||||
// breaks the assumption we have in our application code, see
|
||||
// PremiumListUtils.savePremiumListAndEntries(). Clearing the session cache can help make sure
|
||||
// we always get the same list.
|
||||
tm().clearSessionCache();
|
||||
return transactIfJpaTm(() -> tm().load(premiumList));
|
||||
}
|
||||
|
||||
/** Creates and persists a tld. */
|
||||
@@ -672,17 +708,28 @@ public class DatastoreHelper {
|
||||
}
|
||||
|
||||
private static Iterable<BillingEvent> getBillingEvents() {
|
||||
return Iterables.concat(
|
||||
ofy().load().type(BillingEvent.OneTime.class),
|
||||
ofy().load().type(BillingEvent.Recurring.class),
|
||||
ofy().load().type(BillingEvent.Cancellation.class));
|
||||
return transactIfJpaTm(
|
||||
() ->
|
||||
Iterables.concat(
|
||||
tm().loadAll(BillingEvent.OneTime.class),
|
||||
tm().loadAll(BillingEvent.Recurring.class),
|
||||
tm().loadAll(BillingEvent.Cancellation.class)));
|
||||
}
|
||||
|
||||
private static Iterable<BillingEvent> getBillingEvents(EppResource resource) {
|
||||
return Iterables.concat(
|
||||
ofy().load().type(BillingEvent.OneTime.class).ancestor(resource),
|
||||
ofy().load().type(BillingEvent.Recurring.class).ancestor(resource),
|
||||
ofy().load().type(BillingEvent.Cancellation.class).ancestor(resource));
|
||||
return transactIfJpaTm(
|
||||
() ->
|
||||
Iterables.concat(
|
||||
tm().loadAll(BillingEvent.OneTime.class).stream()
|
||||
.filter(oneTime -> oneTime.getDomainRepoId().equals(resource.getRepoId()))
|
||||
.collect(toImmutableList()),
|
||||
tm().loadAll(BillingEvent.Recurring.class).stream()
|
||||
.filter(recurring -> recurring.getDomainRepoId().equals(resource.getRepoId()))
|
||||
.collect(toImmutableList()),
|
||||
tm().loadAll(BillingEvent.Cancellation.class).stream()
|
||||
.filter(
|
||||
cancellation -> cancellation.getDomainRepoId().equals(resource.getRepoId()))
|
||||
.collect(toImmutableList())));
|
||||
}
|
||||
|
||||
/** Assert that the actual billing event matches the expected one, ignoring IDs. */
|
||||
@@ -751,41 +798,63 @@ public class DatastoreHelper {
|
||||
assertPollMessagesEqual(getPollMessages(), Arrays.asList(expected));
|
||||
}
|
||||
|
||||
public static void assertPollMessagesForResource(EppResource resource, PollMessage... expected) {
|
||||
assertPollMessagesEqual(getPollMessages(resource), Arrays.asList(expected));
|
||||
public static void assertPollMessagesForResource(DomainContent domain, PollMessage... expected) {
|
||||
assertPollMessagesEqual(getPollMessages(domain), Arrays.asList(expected));
|
||||
}
|
||||
|
||||
public static ImmutableList<PollMessage> getPollMessages() {
|
||||
return ImmutableList.copyOf(ofy().load().type(PollMessage.class));
|
||||
return ImmutableList.copyOf(transactIfJpaTm(() -> tm().loadAll(PollMessage.class)));
|
||||
}
|
||||
|
||||
public static ImmutableList<PollMessage> getPollMessages(String clientId) {
|
||||
return ImmutableList.copyOf(ofy().load().type(PollMessage.class).filter("clientId", clientId));
|
||||
return transactIfJpaTm(
|
||||
() ->
|
||||
tm().loadAll(PollMessage.class).stream()
|
||||
.filter(pollMessage -> pollMessage.getClientId().equals(clientId))
|
||||
.collect(toImmutableList()));
|
||||
}
|
||||
|
||||
public static ImmutableList<PollMessage> getPollMessages(EppResource resource) {
|
||||
return ImmutableList.copyOf(ofy().load().type(PollMessage.class).ancestor(resource));
|
||||
public static ImmutableList<PollMessage> getPollMessages(DomainContent domain) {
|
||||
return transactIfJpaTm(
|
||||
() ->
|
||||
tm().loadAll(PollMessage.class).stream()
|
||||
.filter(
|
||||
pollMessage ->
|
||||
pollMessage.getParentKey().getParent().getName().equals(domain.getRepoId()))
|
||||
.collect(toImmutableList()));
|
||||
}
|
||||
|
||||
public static ImmutableList<PollMessage> getPollMessages(String clientId, DateTime now) {
|
||||
return ImmutableList.copyOf(
|
||||
ofy()
|
||||
.load()
|
||||
.type(PollMessage.class)
|
||||
.filter("clientId", clientId)
|
||||
.filter("eventTime <=", now.toDate()));
|
||||
return transactIfJpaTm(
|
||||
() ->
|
||||
tm().loadAll(PollMessage.class).stream()
|
||||
.filter(pollMessage -> pollMessage.getClientId().equals(clientId))
|
||||
.filter(
|
||||
pollMessage ->
|
||||
pollMessage.getEventTime().isEqual(now)
|
||||
|| pollMessage.getEventTime().isBefore(now))
|
||||
.collect(toImmutableList()));
|
||||
}
|
||||
|
||||
/** Gets all PollMessages associated with the given EppResource. */
|
||||
public static ImmutableList<PollMessage> getPollMessages(
|
||||
EppResource resource, String clientId, DateTime now) {
|
||||
return ImmutableList.copyOf(
|
||||
ofy()
|
||||
.load()
|
||||
.type(PollMessage.class)
|
||||
.ancestor(resource)
|
||||
.filter("clientId", clientId)
|
||||
.filter("eventTime <=", now.toDate()));
|
||||
return transactIfJpaTm(
|
||||
() ->
|
||||
tm().loadAll(PollMessage.class).stream()
|
||||
.filter(
|
||||
pollMessage ->
|
||||
pollMessage
|
||||
.getParentKey()
|
||||
.getParent()
|
||||
.getName()
|
||||
.equals(resource.getRepoId()))
|
||||
.filter(pollMessage -> pollMessage.getClientId().equals(clientId))
|
||||
.filter(
|
||||
pollMessage ->
|
||||
pollMessage.getEventTime().isEqual(now)
|
||||
|| pollMessage.getEventTime().isBefore(now))
|
||||
.collect(toImmutableList()));
|
||||
}
|
||||
|
||||
public static PollMessage getOnlyPollMessage(String clientId) {
|
||||
@@ -808,19 +877,15 @@ public class DatastoreHelper {
|
||||
}
|
||||
|
||||
public static PollMessage getOnlyPollMessage(
|
||||
EppResource resource,
|
||||
String clientId,
|
||||
DateTime now,
|
||||
Class<? extends PollMessage> subType) {
|
||||
return getPollMessages(resource, clientId, now)
|
||||
.stream()
|
||||
DomainContent domain, String clientId, DateTime now, Class<? extends PollMessage> subType) {
|
||||
return getPollMessages(domain, clientId, now).stream()
|
||||
.filter(subType::isInstance)
|
||||
.map(subType::cast)
|
||||
.collect(onlyElement());
|
||||
}
|
||||
|
||||
public static void assertAllocationTokens(AllocationToken... expectedTokens) {
|
||||
assertThat(ofy().load().type(AllocationToken.class).list())
|
||||
assertThat(transactIfJpaTm(() -> tm().loadAll(AllocationToken.class)))
|
||||
.comparingElementsUsing(immutableObjectCorrespondence("updateTimestamp", "creationTime"))
|
||||
.containsExactlyElementsIn(expectedTokens);
|
||||
}
|
||||
@@ -861,13 +926,17 @@ public class DatastoreHelper {
|
||||
}
|
||||
|
||||
private static <R> void saveResource(R resource, boolean wantBackup) {
|
||||
Saver saver = wantBackup ? ofy().save() : ofy().saveWithoutBackup();
|
||||
saver.entity(resource);
|
||||
if (resource instanceof EppResource) {
|
||||
EppResource eppResource = (EppResource) resource;
|
||||
persistEppResourceExtras(
|
||||
eppResource, EppResourceIndex.create(Key.create(eppResource)), saver);
|
||||
}
|
||||
ofyOrJpaTm(
|
||||
() -> {
|
||||
Saver saver = wantBackup ? ofy().save() : ofy().saveWithoutBackup();
|
||||
saver.entity(resource);
|
||||
if (resource instanceof EppResource) {
|
||||
EppResource eppResource = (EppResource) resource;
|
||||
persistEppResourceExtras(
|
||||
eppResource, EppResourceIndex.create(Key.create(eppResource)), saver);
|
||||
}
|
||||
},
|
||||
() -> tm().put(resource));
|
||||
}
|
||||
|
||||
private static <R extends EppResource> void persistEppResourceExtras(
|
||||
@@ -890,23 +959,25 @@ public class DatastoreHelper {
|
||||
// Datastore and not from the session cache. This is needed to trigger Objectify's load process
|
||||
// (unmarshalling entity protos to POJOs, nulling out empty collections, calling @OnLoad
|
||||
// methods, etc.) which is bypassed for entities loaded from the session cache.
|
||||
ofy().clearSessionCache();
|
||||
return ofy().load().entity(resource).now();
|
||||
tm().clearSessionCache();
|
||||
return transactIfJpaTm(() -> tm().load(resource));
|
||||
}
|
||||
|
||||
/** Persists an EPP resource with the {@link EppResourceIndex} always going into bucket one. */
|
||||
public static <R extends EppResource> R persistEppResourceInFirstBucket(final R resource) {
|
||||
final EppResourceIndex eppResourceIndex =
|
||||
EppResourceIndex.create(Key.create(EppResourceIndexBucket.class, 1), Key.create(resource));
|
||||
tm()
|
||||
.transact(
|
||||
() -> {
|
||||
Saver saver = ofy().save();
|
||||
saver.entity(resource);
|
||||
persistEppResourceExtras(resource, eppResourceIndex, saver);
|
||||
});
|
||||
ofy().clearSessionCache();
|
||||
return ofy().load().entity(resource).now();
|
||||
tm().transact(
|
||||
() ->
|
||||
ofyOrJpaTm(
|
||||
() -> {
|
||||
Saver saver = ofy().save();
|
||||
saver.entity(resource);
|
||||
persistEppResourceExtras(resource, eppResourceIndex, saver);
|
||||
},
|
||||
() -> tm().put(resource)));
|
||||
tm().clearSessionCache();
|
||||
return transactIfJpaTm(() -> tm().load(resource));
|
||||
}
|
||||
|
||||
public static <R> void persistResources(final Iterable<R> resources) {
|
||||
@@ -925,9 +996,9 @@ public class DatastoreHelper {
|
||||
}
|
||||
// Force the session to be cleared so that when we read it back, we read from Datastore
|
||||
// and not from the transaction's session cache.
|
||||
ofy().clearSessionCache();
|
||||
tm().clearSessionCache();
|
||||
for (R resource : resources) {
|
||||
ofy().load().entity(resource).now();
|
||||
transactIfJpaTm(() -> tm().load(resource));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -943,31 +1014,57 @@ public class DatastoreHelper {
|
||||
*/
|
||||
public static <R extends EppResource> R persistEppResource(final R resource) {
|
||||
checkState(!tm().inTransaction());
|
||||
tm()
|
||||
.transact(
|
||||
tm().transact(
|
||||
() -> {
|
||||
ofy()
|
||||
.save()
|
||||
.<ImmutableObject>entities(
|
||||
resource,
|
||||
tm().put(resource);
|
||||
tm().put(
|
||||
new HistoryEntry.Builder()
|
||||
.setParent(resource)
|
||||
.setType(getHistoryEntryType(resource))
|
||||
.setModificationTime(tm().getTransactionTime())
|
||||
.build());
|
||||
ofy().save().entity(ForeignKeyIndex.create(resource, resource.getDeletionTime()));
|
||||
.build()
|
||||
.toChildHistoryEntity());
|
||||
ofyTmOrDoNothing(
|
||||
() -> tm().put(ForeignKeyIndex.create(resource, resource.getDeletionTime())));
|
||||
});
|
||||
ofy().clearSessionCache();
|
||||
return ofy().load().entity(resource).safe();
|
||||
tm().clearSessionCache();
|
||||
return transactIfJpaTm(() -> tm().load(resource));
|
||||
}
|
||||
|
||||
/** Returns all of the history entries that are parented off the given EppResource. */
|
||||
public static List<HistoryEntry> getHistoryEntries(EppResource resource) {
|
||||
return ofy().load()
|
||||
.type(HistoryEntry.class)
|
||||
.ancestor(resource)
|
||||
.order("modificationTime")
|
||||
.list();
|
||||
public static List<? extends HistoryEntry> getHistoryEntries(EppResource resource) {
|
||||
return ofyOrJpaTm(
|
||||
() ->
|
||||
ofy()
|
||||
.load()
|
||||
.type(HistoryEntry.class)
|
||||
.ancestor(resource)
|
||||
.order("modificationTime")
|
||||
.list(),
|
||||
() ->
|
||||
tm().transact(
|
||||
() -> {
|
||||
ImmutableList<? extends HistoryEntry> unsorted = null;
|
||||
if (resource instanceof ContactBase) {
|
||||
unsorted = tm().loadAll(ContactHistory.class);
|
||||
} else if (resource instanceof HostBase) {
|
||||
unsorted = tm().loadAll(HostHistory.class);
|
||||
} else if (resource instanceof DomainContent) {
|
||||
unsorted = tm().loadAll(DomainHistory.class);
|
||||
} else {
|
||||
fail("Expected an EppResource instance, but got " + resource.getClass());
|
||||
}
|
||||
ImmutableList<? extends HistoryEntry> filtered =
|
||||
unsorted.stream()
|
||||
.filter(
|
||||
historyEntry ->
|
||||
historyEntry
|
||||
.getParent()
|
||||
.getName()
|
||||
.equals(resource.getRepoId()))
|
||||
.collect(toImmutableList());
|
||||
return ImmutableList.sortedCopyOf(DateTimeComparator.getInstance(), filtered);
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1009,9 +1106,13 @@ public class DatastoreHelper {
|
||||
}
|
||||
|
||||
public static PollMessage getOnlyPollMessageForHistoryEntry(HistoryEntry historyEntry) {
|
||||
return Iterables.getOnlyElement(ofy().load()
|
||||
.type(PollMessage.class)
|
||||
.ancestor(historyEntry));
|
||||
return Iterables.getOnlyElement(
|
||||
transactIfJpaTm(
|
||||
() ->
|
||||
tm().loadAll(PollMessage.class).stream()
|
||||
.filter(
|
||||
pollMessage -> pollMessage.getParentKey().equals(Key.create(historyEntry)))
|
||||
.collect(toImmutableList())));
|
||||
}
|
||||
|
||||
public static <T extends EppResource> HistoryEntry createHistoryEntryForEppResource(
|
||||
@@ -1029,23 +1130,30 @@ public class DatastoreHelper {
|
||||
* ForeignKeyedEppResources.
|
||||
*/
|
||||
public static <R> ImmutableList<R> persistSimpleResources(final Iterable<R> resources) {
|
||||
tm().transact(() -> ofy().saveWithoutBackup().entities(resources));
|
||||
tm().transact(() -> tm().putAllWithoutBackup(ImmutableList.copyOf(resources)));
|
||||
// Force the session to be cleared so that when we read it back, we read from Datastore
|
||||
// and not from the transaction's session cache.
|
||||
ofy().clearSessionCache();
|
||||
return ImmutableList.copyOf(ofy().load().entities(resources).values());
|
||||
tm().clearSessionCache();
|
||||
return transactIfJpaTm(() -> tm().loadAll(resources));
|
||||
}
|
||||
|
||||
public static void deleteResource(final Object resource) {
|
||||
ofy().deleteWithoutBackup().entity(resource).now();
|
||||
transactIfJpaTm(() -> tm().deleteWithoutBackup(resource));
|
||||
// Force the session to be cleared so that when we read it back, we read from Datastore and
|
||||
// not from the transaction's session cache.
|
||||
ofy().clearSessionCache();
|
||||
tm().clearSessionCache();
|
||||
}
|
||||
|
||||
/** Force the create and update timestamps to get written into the resource. **/
|
||||
public static <R> R cloneAndSetAutoTimestamps(final R resource) {
|
||||
return tm().transact(() -> ofy().load().fromEntity(ofy().save().toEntity(resource)));
|
||||
return tm().transact(
|
||||
() ->
|
||||
ofyOrJpaTm(
|
||||
() -> ofy().load().fromEntity(ofy().save().toEntity(resource)),
|
||||
() -> {
|
||||
tm().put(resource);
|
||||
return tm().load(resource);
|
||||
}));
|
||||
}
|
||||
|
||||
/** Returns the entire map of {@link PremiumListEntry}s for the given {@link PremiumList}. */
|
||||
|
||||
+28
-3
@@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import google.registry.persistence.transaction.TransactionManager;
|
||||
import google.registry.persistence.transaction.TransactionManagerFactory;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
@@ -52,9 +53,21 @@ class DualDatabaseTestInvocationContextProvider implements TestTemplateInvocatio
|
||||
@Override
|
||||
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
|
||||
ExtensionContext context) {
|
||||
return Stream.of(
|
||||
createInvocationContext("Test Datastore", TransactionManagerFactory::ofyTm),
|
||||
createInvocationContext("Test PostgreSQL", TransactionManagerFactory::jpaTm));
|
||||
TestTemplateInvocationContext ofyContext =
|
||||
createInvocationContext("Test Datastore", TransactionManagerFactory::ofyTm);
|
||||
TestTemplateInvocationContext sqlContext =
|
||||
createInvocationContext("Test PostgreSQL", TransactionManagerFactory::jpaTm);
|
||||
Method testMethod = context.getTestMethod().orElseThrow(IllegalStateException::new);
|
||||
if (testMethod.isAnnotationPresent(TestOfyAndSql.class)) {
|
||||
return Stream.of(ofyContext, sqlContext);
|
||||
} else if (testMethod.isAnnotationPresent(TestOfyOnly.class)) {
|
||||
return Stream.of(ofyContext);
|
||||
} else if (testMethod.isAnnotationPresent(TestSqlOnly.class)) {
|
||||
return Stream.of(sqlContext);
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Test method must be annotated with @TestOfyAndSql, @TestOfyOnly or @TestSqlOnly");
|
||||
}
|
||||
}
|
||||
|
||||
private TestTemplateInvocationContext createInvocationContext(
|
||||
@@ -104,6 +117,18 @@ class DualDatabaseTestInvocationContextProvider implements TestTemplateInvocatio
|
||||
|
||||
static void injectTmForDualDatabaseTest(ExtensionContext context) {
|
||||
if (isDualDatabaseTest(context)) {
|
||||
context
|
||||
.getTestMethod()
|
||||
.ifPresent(
|
||||
testMethod -> {
|
||||
if (!testMethod.isAnnotationPresent(TestOfyAndSql.class)
|
||||
&& !testMethod.isAnnotationPresent(TestOfyOnly.class)
|
||||
&& !testMethod.isAnnotationPresent(TestSqlOnly.class)) {
|
||||
throw new IllegalStateException(
|
||||
"Test method must be annotated with @TestOfyAndSql, @TestOfyOnly or"
|
||||
+ " @TestSqlOnly");
|
||||
}
|
||||
});
|
||||
context.getStore(NAMESPACE).put(ORIGINAL_TM_KEY, tm());
|
||||
Supplier<? extends TransactionManager> tmSupplier =
|
||||
(Supplier<? extends TransactionManager>)
|
||||
|
||||
+41
-11
@@ -19,37 +19,67 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
|
||||
import google.registry.model.ofy.DatastoreTransactionManager;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import google.registry.persistence.transaction.TransactionManager;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.TestTemplate;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/**
|
||||
* Test to verify that {@link DualDatabaseTestInvocationContextProvider} extension executes {@link
|
||||
* TestTemplate} test twice with different databases.
|
||||
* Test to verify that {@link DualDatabaseTestInvocationContextProvider} extension executes tests
|
||||
* with corresponding {@link TransactionManager}.
|
||||
*/
|
||||
@DualDatabaseTest
|
||||
public class DualDatabaseTestInvocationContextProviderTest {
|
||||
|
||||
private static int datastoreTestCounter = 0;
|
||||
private static int postgresqlTestCounter = 0;
|
||||
private static int testBothDbsOfyCounter = 0;
|
||||
private static int testBothDbsSqlCounter = 0;
|
||||
private static int testOfyOnlyOfyCounter = 0;
|
||||
private static int testOfyOnlySqlCounter = 0;
|
||||
private static int testSqlOnlyOfyCounter = 0;
|
||||
private static int testSqlOnlySqlCounter = 0;
|
||||
|
||||
@RegisterExtension
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withDatastoreAndCloudSql().build();
|
||||
|
||||
@TestTemplate
|
||||
void testToUseTransactionManager() {
|
||||
@TestOfyAndSql
|
||||
void testToVerifyBothOfyAndSqlTmAreUsed() {
|
||||
if (tm() instanceof DatastoreTransactionManager) {
|
||||
datastoreTestCounter++;
|
||||
testBothDbsOfyCounter++;
|
||||
}
|
||||
if (tm() instanceof JpaTransactionManager) {
|
||||
postgresqlTestCounter++;
|
||||
testBothDbsSqlCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
@TestOfyOnly
|
||||
void testToVerifyOnlyOfyTmIsUsed() {
|
||||
if (tm() instanceof DatastoreTransactionManager) {
|
||||
testOfyOnlyOfyCounter++;
|
||||
}
|
||||
if (tm() instanceof JpaTransactionManager) {
|
||||
testOfyOnlySqlCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
@TestSqlOnly
|
||||
void testToVerifyOnlySqlTmIsUsed() {
|
||||
if (tm() instanceof DatastoreTransactionManager) {
|
||||
testSqlOnlyOfyCounter++;
|
||||
}
|
||||
if (tm() instanceof JpaTransactionManager) {
|
||||
testSqlOnlySqlCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void assertEachTransactionManagerIsUsed() {
|
||||
assertThat(datastoreTestCounter).isEqualTo(1);
|
||||
assertThat(postgresqlTestCounter).isEqualTo(1);
|
||||
assertThat(testBothDbsOfyCounter).isEqualTo(1);
|
||||
assertThat(testBothDbsSqlCounter).isEqualTo(1);
|
||||
|
||||
assertThat(testOfyOnlyOfyCounter).isEqualTo(1);
|
||||
assertThat(testOfyOnlySqlCounter).isEqualTo(0);
|
||||
|
||||
assertThat(testSqlOnlyOfyCounter).isEqualTo(0);
|
||||
assertThat(testSqlOnlySqlCounter).isEqualTo(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.testing;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
import org.junit.jupiter.api.TestTemplate;
|
||||
|
||||
/**
|
||||
* Annotation to indicate a test method will be executed twice with Datastore and Postgresql
|
||||
* respectively.
|
||||
*/
|
||||
@Target({METHOD})
|
||||
@Retention(RUNTIME)
|
||||
@TestTemplate
|
||||
public @interface TestOfyAndSql {}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.testing;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
import org.junit.jupiter.api.TestTemplate;
|
||||
|
||||
/** Annotation to indicate a test method will be executed only with Datastore. */
|
||||
@Target({METHOD})
|
||||
@Retention(RUNTIME)
|
||||
@TestTemplate
|
||||
public @interface TestOfyOnly {}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.testing;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
import org.junit.jupiter.api.TestTemplate;
|
||||
|
||||
/** Annotation to indicate a test method will be executed only with Postgresql. */
|
||||
@Target({METHOD})
|
||||
@Retention(RUNTIME)
|
||||
@TestTemplate
|
||||
public @interface TestSqlOnly {}
|
||||
@@ -37,8 +37,8 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Range;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.flows.certs.CertificateChecker;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.util.CertificateChecker;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
+4
-4
@@ -38,9 +38,9 @@ import org.joda.money.Money;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link ResaveEntitiesWithUniqueIdCommand}. */
|
||||
class ResaveEntitiesWithUniqueIdCommandTest
|
||||
extends CommandTestCase<ResaveEntitiesWithUniqueIdCommand> {
|
||||
/** Unit tests for {@link DedupeOneTimeBillingEventIdsCommand}. */
|
||||
class DedupeOneTimeBillingEventIdsCommandTest
|
||||
extends CommandTestCase<DedupeOneTimeBillingEventIdsCommand> {
|
||||
|
||||
DomainBase domain;
|
||||
HistoryEntry historyEntry;
|
||||
@@ -48,7 +48,7 @@ class ResaveEntitiesWithUniqueIdCommandTest
|
||||
BillingEvent.OneTime billingEventToResave;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
void beforeEach() {
|
||||
createTld("foobar");
|
||||
domain = persistActiveDomain("foo.foobar");
|
||||
historyEntry = persistHistoryEntry(domain);
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.testing.DatastoreHelper.createTld;
|
||||
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatastoreHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.transfer.DomainTransferData;
|
||||
import java.util.Arrays;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link DedupeRecurringBillingEventIdsCommand}. */
|
||||
class DedupeRecurringBillingEventIdsCommandTest
|
||||
extends CommandTestCase<DedupeRecurringBillingEventIdsCommand> {
|
||||
|
||||
private final DateTime now = DateTime.now(UTC);
|
||||
private DomainBase domain1;
|
||||
private DomainBase domain2;
|
||||
private HistoryEntry historyEntry1;
|
||||
private HistoryEntry historyEntry2;
|
||||
private BillingEvent.Recurring recurring1;
|
||||
private BillingEvent.Recurring recurring2;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
domain1 = persistActiveDomain("foo.tld");
|
||||
domain2 = persistActiveDomain("bar.tld");
|
||||
historyEntry1 =
|
||||
persistResource(
|
||||
new HistoryEntry.Builder().setParent(domain1).setModificationTime(now).build());
|
||||
historyEntry2 =
|
||||
persistResource(
|
||||
new HistoryEntry.Builder()
|
||||
.setParent(domain2)
|
||||
.setModificationTime(now.plusDays(1))
|
||||
.build());
|
||||
recurring1 =
|
||||
persistResource(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setParent(historyEntry1)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.setClientId("a registrar")
|
||||
.setTargetId("foo.tld")
|
||||
.build());
|
||||
recurring2 =
|
||||
persistResource(
|
||||
new BillingEvent.Recurring.Builder()
|
||||
.setId(recurring1.getId())
|
||||
.setParent(historyEntry2)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setReason(Reason.RENEW)
|
||||
.setEventTime(now.plusYears(1))
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.setClientId("a registrar")
|
||||
.setTargetId("bar.tld")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOnlyResaveBillingEventsCorrectly() throws Exception {
|
||||
assertThat(recurring1.getId()).isEqualTo(recurring2.getId());
|
||||
|
||||
runCommand(
|
||||
"--force",
|
||||
"--key_paths_file",
|
||||
writeToNamedTmpFile("keypath.txt", getKeyPathLiteral(recurring1, recurring2)));
|
||||
|
||||
assertNotChangeExceptUpdateTime(domain1, domain2, historyEntry1, historyEntry2);
|
||||
assertNotInDatastore(recurring1, recurring2);
|
||||
|
||||
ImmutableList<BillingEvent.Recurring> recurrings = loadAllRecurrings();
|
||||
assertThat(recurrings.size()).isEqualTo(2);
|
||||
|
||||
recurrings.forEach(
|
||||
newRecurring -> {
|
||||
if (newRecurring.getTargetId().equals("foo.tld")) {
|
||||
assertSameRecurringEntityExceptId(newRecurring, recurring1);
|
||||
} else if (newRecurring.getTargetId().equals("bar.tld")) {
|
||||
assertSameRecurringEntityExceptId(newRecurring, recurring2);
|
||||
} else {
|
||||
fail("Unknown BillingEvent.Recurring entity: " + newRecurring.createVKey());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testResaveAssociatedDomainAndOneTimeBillingEventCorrectly() throws Exception {
|
||||
assertThat(recurring1.getId()).isEqualTo(recurring2.getId());
|
||||
domain1 =
|
||||
persistResource(
|
||||
domain1
|
||||
.asBuilder()
|
||||
.setAutorenewBillingEvent(recurring1.createVKey())
|
||||
.setGracePeriods(
|
||||
ImmutableSet.of(
|
||||
GracePeriod.createForRecurring(
|
||||
GracePeriodStatus.AUTO_RENEW,
|
||||
domain1.getRepoId(),
|
||||
now.plusDays(45),
|
||||
"a registrar",
|
||||
recurring1.createVKey())))
|
||||
.setTransferData(
|
||||
new DomainTransferData.Builder()
|
||||
.setServerApproveAutorenewEvent(recurring1.createVKey())
|
||||
.setServerApproveEntities(ImmutableSet.of(recurring1.createVKey()))
|
||||
.build())
|
||||
.build());
|
||||
|
||||
BillingEvent.OneTime oneTime =
|
||||
persistResource(
|
||||
new BillingEvent.OneTime.Builder()
|
||||
.setClientId("a registrar")
|
||||
.setTargetId("foo.tld")
|
||||
.setParent(historyEntry1)
|
||||
.setReason(Reason.CREATE)
|
||||
.setFlags(ImmutableSet.of(Flag.SYNTHETIC))
|
||||
.setSyntheticCreationTime(now)
|
||||
.setPeriodYears(2)
|
||||
.setCost(Money.of(USD, 1))
|
||||
.setEventTime(now)
|
||||
.setBillingTime(now.plusDays(5))
|
||||
.setCancellationMatchingBillingEvent(recurring1.createVKey())
|
||||
.build());
|
||||
|
||||
runCommand(
|
||||
"--force",
|
||||
"--key_paths_file",
|
||||
writeToNamedTmpFile("keypath.txt", getKeyPathLiteral(recurring1, recurring2)));
|
||||
|
||||
assertNotChangeExceptUpdateTime(domain2, historyEntry1, historyEntry2);
|
||||
assertNotInDatastore(recurring1, recurring2);
|
||||
ImmutableList<BillingEvent.Recurring> recurrings = loadAllRecurrings();
|
||||
assertThat(recurrings.size()).isEqualTo(2);
|
||||
|
||||
recurrings.forEach(
|
||||
newRecurring -> {
|
||||
if (newRecurring.getTargetId().equals("foo.tld")) {
|
||||
assertSameRecurringEntityExceptId(newRecurring, recurring1);
|
||||
|
||||
BillingEvent.OneTime persistedOneTime = ofy().load().entity(oneTime).now();
|
||||
assertAboutImmutableObjects()
|
||||
.that(persistedOneTime)
|
||||
.isEqualExceptFields(oneTime, "cancellationMatchingBillingEvent");
|
||||
assertThat(persistedOneTime.getCancellationMatchingBillingEvent())
|
||||
.isEqualTo(newRecurring.createVKey());
|
||||
|
||||
DomainBase persistedDomain = ofy().load().entity(domain1).now();
|
||||
assertAboutImmutableObjects()
|
||||
.that(persistedDomain)
|
||||
.isEqualExceptFields(
|
||||
domain1,
|
||||
"updateTimestamp",
|
||||
"revisions",
|
||||
"gracePeriods",
|
||||
"transferData",
|
||||
"autorenewBillingEvent");
|
||||
assertThat(persistedDomain.getAutorenewBillingEvent())
|
||||
.isEqualTo(newRecurring.createVKey());
|
||||
assertThat(persistedDomain.getGracePeriods())
|
||||
.containsExactly(
|
||||
GracePeriod.createForRecurring(
|
||||
GracePeriodStatus.AUTO_RENEW,
|
||||
domain1.getRepoId(),
|
||||
now.plusDays(45),
|
||||
"a registrar",
|
||||
newRecurring.createVKey()));
|
||||
assertThat(persistedDomain.getTransferData().getServerApproveAutorenewEvent())
|
||||
.isEqualTo(newRecurring.createVKey());
|
||||
assertThat(persistedDomain.getTransferData().getServerApproveEntities())
|
||||
.containsExactly(newRecurring.createVKey());
|
||||
|
||||
} else if (newRecurring.getTargetId().equals("bar.tld")) {
|
||||
assertSameRecurringEntityExceptId(newRecurring, recurring2);
|
||||
} else {
|
||||
fail("Unknown BillingEvent.Recurring entity: " + newRecurring.createVKey());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void assertNotInDatastore(ImmutableObject... entities) {
|
||||
for (ImmutableObject entity : entities) {
|
||||
assertThat(ofy().load().entity(entity).now()).isNull();
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertNotChangeInDatastore(ImmutableObject... entities) {
|
||||
for (ImmutableObject entity : entities) {
|
||||
assertThat(ofy().load().entity(entity).now()).isEqualTo(entity);
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertNotChangeExceptUpdateTime(ImmutableObject... entities) {
|
||||
for (ImmutableObject entity : entities) {
|
||||
assertAboutImmutableObjects()
|
||||
.that(ofy().load().entity(entity).now())
|
||||
.isEqualExceptFields(entity, "updateTimestamp", "revisions");
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertSameRecurringEntityExceptId(
|
||||
BillingEvent.Recurring recurring1, BillingEvent.Recurring recurring2) {
|
||||
assertAboutImmutableObjects().that(recurring1).isEqualExceptFields(recurring2, "id");
|
||||
}
|
||||
|
||||
private static ImmutableList<BillingEvent.Recurring> loadAllRecurrings() {
|
||||
return ImmutableList.copyOf(ofy().load().type(BillingEvent.Recurring.class));
|
||||
}
|
||||
|
||||
private static String getKeyPathLiteral(Object... entities) {
|
||||
return Arrays.stream(entities)
|
||||
.map(
|
||||
entity -> {
|
||||
Key<?> key = Key.create(entity);
|
||||
return String.format(
|
||||
"\"DomainBase\", \"%s\", \"HistoryEntry\", %s, \"%s\", %s",
|
||||
key.getParent().getParent().getName(),
|
||||
key.getParent().getId(),
|
||||
key.getKind(),
|
||||
key.getId());
|
||||
})
|
||||
.reduce((k1, k2) -> k1 + "\n" + k2)
|
||||
.get();
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ class EppLifecycleToolsTest extends EppTestCase {
|
||||
.atTime("2001-06-08T00:00:00Z")
|
||||
.hasResponse("poll_response_unrenew.xml");
|
||||
|
||||
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-8-TLD-17-18-2001"))
|
||||
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-8-TLD-23-24-2001"))
|
||||
.atTime("2001-06-08T00:00:01Z")
|
||||
.hasResponse("poll_ack_response_empty.xml");
|
||||
|
||||
@@ -129,7 +129,7 @@ class EppLifecycleToolsTest extends EppTestCase {
|
||||
.hasResponse(
|
||||
"poll_response_autorenew.xml",
|
||||
ImmutableMap.of(
|
||||
"ID", "1-8-TLD-17-20-2003",
|
||||
"ID", "1-8-TLD-23-26-2003",
|
||||
"QDATE", "2003-06-01T00:02:00Z",
|
||||
"DOMAIN", "example.tld",
|
||||
"EXDATE", "2004-06-01T00:02:00Z"));
|
||||
|
||||
@@ -34,12 +34,12 @@ 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 google.registry.flows.certs.CertificateChecker;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.Registrar.State;
|
||||
import google.registry.model.registrar.Registrar.Type;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.util.CertificateChecker;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
-78
@@ -376,84 +376,6 @@ class RegistrarSettingsActionTest extends RegistrarSettingsActionTestCase {
|
||||
(builder, s) -> builder.setClientCertificate(s, clock.nowUtc()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdate_clientCertificateWithViolationsBeforeNovemberSucceeds() {
|
||||
// TODO(sarahbot): remove this test after November 1, 2020.
|
||||
clock.setTo(DateTime.parse("2018-07-02T00:00:00Z"));
|
||||
doTestUpdate(
|
||||
Role.OWNER,
|
||||
Registrar::getClientCertificate,
|
||||
CertificateSamples.SAMPLE_CERT,
|
||||
(builder, s) -> builder.setClientCertificate(s, clock.nowUtc()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdate_otherFieldsWhenClientCertificateWithViolationsAlreadyExistedSucceeds() {
|
||||
// TODO(sarahbot): remove this test after November 1, 2020.
|
||||
|
||||
// The frontend will always send the entire registrar entity back for an update, so the checks
|
||||
// on the certificate should only run if a new certificate is being uploaded. All other updates
|
||||
// after November 1st should still succeed even if a bad certificate is stored.
|
||||
|
||||
// Set a bad certificate before checks on uploads are enforced
|
||||
clock.setTo(DateTime.parse("2018-07-02T00:00:00Z"));
|
||||
Registrar existingRegistrar = loadRegistrar(CLIENT_ID);
|
||||
existingRegistrar =
|
||||
existingRegistrar
|
||||
.asBuilder()
|
||||
.setClientCertificate(CertificateSamples.SAMPLE_CERT, clock.nowUtc())
|
||||
.build();
|
||||
persistResource(existingRegistrar);
|
||||
|
||||
// Update the other registrar fields after enforcement begins should succeed
|
||||
clock.setTo(DateTime.parse("2020-11-02T00:00:00Z"));
|
||||
Map<String, Object> args = Maps.newHashMap(loadRegistrar(CLIENT_ID).toJsonMap());
|
||||
args.put("url", "test.url");
|
||||
args.put("phoneNumber", "+1.1234567890");
|
||||
Map<String, Object> response =
|
||||
action.handleJsonRequest(
|
||||
ImmutableMap.of(
|
||||
"op", "update",
|
||||
"id", CLIENT_ID,
|
||||
"args", args));
|
||||
|
||||
assertThat(response).containsEntry("status", "SUCCESS");
|
||||
assertMetric(CLIENT_ID, "update", "[OWNER]", "SUCCESS");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdate_clientCertificateWithViolationsAlreadyExistedSucceeds() {
|
||||
// TODO(sarahbot): remove this test after November 1, 2020.
|
||||
|
||||
// The frontend will always send the entire registrar entity back for an update, so the checks
|
||||
// on the certificate should only run if it is a new certificate
|
||||
|
||||
// Set a bad certificate before checks on uploads are enforced
|
||||
clock.setTo(DateTime.parse("2018-07-02T00:00:00Z"));
|
||||
Registrar existingRegistrar = loadRegistrar(CLIENT_ID);
|
||||
existingRegistrar =
|
||||
existingRegistrar
|
||||
.asBuilder()
|
||||
.setClientCertificate(CertificateSamples.SAMPLE_CERT, clock.nowUtc())
|
||||
.build();
|
||||
persistResource(existingRegistrar);
|
||||
|
||||
// Update with the same certificate after enforcement starts
|
||||
clock.setTo(DateTime.parse("2020-11-02T00:00:00Z"));
|
||||
Map<String, Object> args = Maps.newHashMap(loadRegistrar(CLIENT_ID).toJsonMap());
|
||||
args.put("clientCertificate", CertificateSamples.SAMPLE_CERT);
|
||||
Map<String, Object> response =
|
||||
action.handleJsonRequest(
|
||||
ImmutableMap.of(
|
||||
"op", "update",
|
||||
"id", CLIENT_ID,
|
||||
"args", args));
|
||||
|
||||
assertThat(response).containsEntry("status", "SUCCESS");
|
||||
assertMetric(CLIENT_ID, "update", "[OWNER]", "SUCCESS");
|
||||
assertNoTasksEnqueued("sheet");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdate_clientCertificateWithViolationsFails() {
|
||||
clock.setTo(DateTime.parse("2020-11-02T00:00:00Z"));
|
||||
|
||||
+1
-1
@@ -34,6 +34,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.truth.Truth;
|
||||
import google.registry.flows.certs.CertificateChecker;
|
||||
import google.registry.model.ofy.Ofy;
|
||||
import google.registry.model.registrar.RegistrarContact;
|
||||
import google.registry.request.JsonActionRunner;
|
||||
@@ -48,7 +49,6 @@ import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import google.registry.ui.server.SendEmailUtils;
|
||||
import google.registry.util.AppEngineServiceUtils;
|
||||
import google.registry.util.CertificateChecker;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.SendEmailService;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
<result code="1301">
|
||||
<msg>Command completed successfully; ack to dequeue</msg>
|
||||
</result>
|
||||
<msgQ count="1" id="1-C-EXAMPLE-17-23-2001">
|
||||
<msgQ count="1" id="%ID%">
|
||||
<qDate>2001-01-01T00:00:00Z</qDate>
|
||||
<msg>Transfer requested.</msg>
|
||||
</msgQ>
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
<result code="1301">
|
||||
<msg>Command completed successfully; ack to dequeue</msg>
|
||||
</result>
|
||||
<msgQ count="1" id="1-C-EXAMPLE-17-22-2001">
|
||||
<msgQ count="1" id="%ID%">
|
||||
<qDate>2001-01-06T00:00:00Z</qDate>
|
||||
<msg>Transfer approved.</msg>
|
||||
</msgQ>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<result code="1301">
|
||||
<msg>Command completed successfully; ack to dequeue</msg>
|
||||
</result>
|
||||
<msgQ count="1" id="1-C-EXAMPLE-17-21-2001">
|
||||
<msgQ count="1" id="%ID%">
|
||||
<qDate>2001-01-06T00:00:00Z</qDate>
|
||||
<msg>Transfer approved.</msg>
|
||||
</msgQ>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<result code="1301">
|
||||
<msg>Command completed successfully; ack to dequeue</msg>
|
||||
</result>
|
||||
<msgQ count="1" id="1-8-TLD-17-18-2001">
|
||||
<msgQ count="1" id="1-8-TLD-23-24-2001">
|
||||
<qDate>2001-06-07T00:00:00Z</qDate>
|
||||
<msg>Domain example.tld was unrenewed by 3 years; now expires at 2003-06-01T00:02:00.000Z.</msg>
|
||||
</msgQ>
|
||||
|
||||
@@ -273,6 +273,7 @@ class google.registry.model.domain.DomainHistory {
|
||||
java.lang.String clientId;
|
||||
java.lang.String otherClientId;
|
||||
java.lang.String reason;
|
||||
java.util.Set<google.registry.model.domain.secdns.DomainDsDataHistory> dsDataHistories;
|
||||
java.util.Set<google.registry.model.reporting.DomainTransactionRecord> domainTransactionRecords;
|
||||
org.joda.time.DateTime modificationTime;
|
||||
}
|
||||
@@ -315,6 +316,14 @@ class google.registry.model.domain.secdns.DelegationSignerData {
|
||||
int digestType;
|
||||
int keyTag;
|
||||
}
|
||||
class google.registry.model.domain.secdns.DomainDsDataHistory {
|
||||
byte[] digest;
|
||||
int algorithm;
|
||||
int digestType;
|
||||
int keyTag;
|
||||
java.lang.Long domainHistoryRevisionId;
|
||||
java.lang.Long dsDataHistoryRevisionId;
|
||||
}
|
||||
class google.registry.model.domain.token.AllocationToken {
|
||||
@Id java.lang.String token;
|
||||
boolean discountPremiums;
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
This project contains Nomulus's Cloud SQL schema and schema-deployment
|
||||
utilities.
|
||||
|
||||
### ER Diagrams
|
||||
|
||||
The following links are the ER diagrams generated from the current SQL schema:
|
||||
|
||||
* [Full ER diagram](https://storage.googleapis.com/domain-registry-dev-er-diagram/full_er_diagram.html):
|
||||
shows all columns, foreign keys and indexes.
|
||||
|
||||
* [Brief ER diagram](https://storage.googleapis.com/domain-registry-dev-er-diagram/brief_er_diagram.html):
|
||||
shows only significant columns, such as primary and foreign key columns, and
|
||||
columns that are part of unique indexes.
|
||||
|
||||
### Database Roles and Privileges
|
||||
|
||||
Nomulus uses the 'postgres' database in the 'public' schema. The following
|
||||
|
||||
@@ -34,10 +34,11 @@ io.opencensus:opencensus-contrib-http-util:0.24.0
|
||||
org.apache.httpcomponents:httpclient:4.5.10
|
||||
org.apache.httpcomponents:httpcore:4.4.12
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:3.5.0
|
||||
org.flywaydb:flyway-core:5.2.4
|
||||
org.ow2.asm:asm-analysis:8.0.1
|
||||
org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
|
||||
@@ -34,10 +34,11 @@ io.opencensus:opencensus-contrib-http-util:0.24.0
|
||||
org.apache.httpcomponents:httpclient:4.5.10
|
||||
org.apache.httpcomponents:httpcore:4.4.12
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:3.5.0
|
||||
org.flywaydb:flyway-core:5.2.4
|
||||
org.ow2.asm:asm-analysis:8.0.1
|
||||
org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
|
||||
@@ -34,10 +34,11 @@ io.opencensus:opencensus-contrib-http-util:0.24.0
|
||||
org.apache.httpcomponents:httpclient:4.5.10
|
||||
org.apache.httpcomponents:httpcore:4.4.12
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:3.5.0
|
||||
org.flywaydb:flyway-core:5.2.4
|
||||
org.ow2.asm:asm-analysis:8.0.1
|
||||
org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
|
||||
@@ -46,7 +46,7 @@ org.apache.httpcomponents:httpclient:4.5.10
|
||||
org.apache.httpcomponents:httpcore:4.4.12
|
||||
org.apiguardian:apiguardian-api:1.1.0
|
||||
org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.11.1
|
||||
org.checkerframework:checker-qual:3.5.0
|
||||
org.eclipse.jgit:org.eclipse.jgit:4.4.1.201607150455-r
|
||||
org.flywaydb:flyway-core:5.2.4
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
@@ -62,7 +62,7 @@ org.ow2.asm:asm-commons:7.1
|
||||
org.ow2.asm:asm-tree:8.0.1
|
||||
org.ow2.asm:asm-util:8.0.1
|
||||
org.ow2.asm:asm:8.0.1
|
||||
org.postgresql:postgresql:42.2.14
|
||||
org.postgresql:postgresql:42.2.18
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -64,3 +64,8 @@ V63__add_schema_for_ds_data.sql
|
||||
V64__transfer_history_columns.sql
|
||||
V65__local_date_date_type.sql
|
||||
V66__create_rde_revision.sql
|
||||
V67__grace_period_history_ids.sql
|
||||
V68__make_reserved_list_nullable_in_registry.sql
|
||||
V69__change_primary_key_and_add_history_table_for_delegation_signer.sql
|
||||
V70__signed_mark_revocation_list.sql
|
||||
V71__create_kms_secret.sql
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
-- Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
--
|
||||
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||
-- you may not use this file except in compliance with the License.
|
||||
-- You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing, software
|
||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
|
||||
ALTER TABLE "GracePeriod" ADD COLUMN billing_event_history_id int8;
|
||||
ALTER TABLE "GracePeriod" ADD COLUMN billing_recurrence_history_id int8;
|
||||
|
||||
ALTER TABLE ONLY public."GracePeriod"
|
||||
DROP CONSTRAINT fk2mys4hojm6ev2g9tmy5aq6m7g;
|
||||
ALTER TABLE ONLY public."GracePeriod"
|
||||
ADD CONSTRAINT fk_grace_period_domain_repo_id
|
||||
FOREIGN KEY (domain_repo_id) REFERENCES public."Domain"(repo_id)
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
|
||||
ALTER TABLE "GracePeriod" ALTER COLUMN id drop default;
|
||||
DROP SEQUENCE "GracePeriod_id_seq";
|
||||
@@ -0,0 +1,15 @@
|
||||
-- Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
--
|
||||
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||
-- you may not use this file except in compliance with the License.
|
||||
-- You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing, software
|
||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
|
||||
ALTER TABLE "Tld" ALTER COLUMN reserved_list_names DROP NOT NULL;
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
-- Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
--
|
||||
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||
-- you may not use this file except in compliance with the License.
|
||||
-- You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing, software
|
||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
|
||||
alter table "DelegationSignerData" drop constraint "DelegationSignerData_pkey";
|
||||
|
||||
alter table "DelegationSignerData"
|
||||
add constraint "DelegationSignerData_pkey"
|
||||
primary key (domain_repo_id, key_tag, algorithm, digest_type, digest);
|
||||
|
||||
create table "DomainDsDataHistory" (
|
||||
ds_data_history_revision_id int8 not null,
|
||||
algorithm int4 not null,
|
||||
digest bytea not null,
|
||||
digest_type int4 not null,
|
||||
domain_history_revision_id int8 not null,
|
||||
key_tag int4 not null,
|
||||
domain_repo_id text,
|
||||
primary key (ds_data_history_revision_id)
|
||||
);
|
||||
|
||||
alter table if exists "DomainDsDataHistory"
|
||||
add constraint FKo4ilgyyfnvppbpuivus565i0j
|
||||
foreign key (domain_repo_id, domain_history_revision_id)
|
||||
references "DomainHistory";
|
||||
@@ -0,0 +1,31 @@
|
||||
-- Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
--
|
||||
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||
-- you may not use this file except in compliance with the License.
|
||||
-- You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing, software
|
||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
|
||||
create table "SignedMarkRevocationEntry" (
|
||||
revision_id int8 not null,
|
||||
revocation_time timestamptz not null,
|
||||
smd_id text not null,
|
||||
primary key (revision_id, smd_id)
|
||||
);
|
||||
|
||||
create table "SignedMarkRevocationList" (
|
||||
revision_id bigserial not null,
|
||||
creation_time timestamptz,
|
||||
primary key (revision_id)
|
||||
);
|
||||
|
||||
alter table if exists "SignedMarkRevocationEntry"
|
||||
add constraint FK5ivlhvs3121yx2li5tqh54u4
|
||||
foreign key (revision_id)
|
||||
references "SignedMarkRevocationList";
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user