diff --git a/common/build.gradle b/common/build.gradle index 6ce5873c6..d8cec296f 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -58,6 +58,7 @@ dependencies { implementation deps['com.google.code.findbugs:jsr305'] implementation deps['com.google.guava:guava'] implementation deps['jakarta.inject:jakarta.inject-api'] + implementation deps['org.freemarker:freemarker'] implementation deps['com.google.flogger:flogger'] implementation deps['io.github.java-diff-utils:java-diff-utils'] implementation deps['com.google.truth:truth'] diff --git a/common/gradle.lockfile b/common/gradle.lockfile index eea2bea6b..040629b7b 100644 --- a/common/gradle.lockfile +++ b/common/gradle.lockfile @@ -34,7 +34,7 @@ commons-collections:commons-collections:3.2.2=checkstyle info.picocli:picocli:4.7.7=checkstyle io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor,testingAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor,testingAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.16=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath +io.github.java-diff-utils:java-diff-utils:4.17=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath jakarta.inject:jakarta.inject-api:2.0.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath javax.inject:javax.inject:1=annotationProcessor,testAnnotationProcessor,testingAnnotationProcessor junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath @@ -60,6 +60,7 @@ org.codehaus.plexus:plexus-classworlds:2.6.0=checkstyle org.codehaus.plexus:plexus-component-annotations:2.1.0=checkstyle org.codehaus.plexus:plexus-container-default:2.1.0=checkstyle org.codehaus.plexus:plexus-utils:3.3.0=checkstyle +org.freemarker:freemarker:2.3.34=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath org.jacoco:org.jacoco.agent:0.8.14=jacocoAgent,jacocoAnt org.jacoco:org.jacoco.ant:0.8.14=jacocoAnt diff --git a/common/src/main/java/google/registry/util/TemplateRenderer.java b/common/src/main/java/google/registry/util/TemplateRenderer.java new file mode 100644 index 000000000..56dadfd60 --- /dev/null +++ b/common/src/main/java/google/registry/util/TemplateRenderer.java @@ -0,0 +1,67 @@ +// Copyright 2026 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.util; + +import com.google.common.collect.ImmutableMap; +import freemarker.core.HTMLOutputFormat; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateExceptionHandler; +import jakarta.inject.Inject; +import java.io.StringWriter; + +/** + * A utility class for rendering FreeMarker templates. + * + *

This renderer is configured to use HTML as the default output format, which enables automatic + * escaping of all interpolated variables. It also uses the "computer" number format to ensure + * consistent formatting of numeric values across different locales. + */ +public class TemplateRenderer { + + private final Configuration configuration; + + @Inject + public TemplateRenderer() { + this.configuration = new Configuration(Configuration.VERSION_2_3_32); + this.configuration.setClassLoaderForTemplateLoading(getClass().getClassLoader(), ""); + this.configuration.setDefaultEncoding("UTF-8"); + this.configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + this.configuration.setLogTemplateExceptions(false); + this.configuration.setWrapUncheckedExceptions(true); + this.configuration.setFallbackOnNullLoopVariable(false); + this.configuration.setOutputFormat(HTMLOutputFormat.INSTANCE); + this.configuration.setNumberFormat("computer"); + } + + /** + * Renders the specified template with the given data model. + * + * @param templatePath the path to the template file relative to the classpath root + * @param dataModel an immutable map containing the data to be used in the template + * @return the rendered template as a string + * @throws RuntimeException if the template cannot be found, parsed, or processed + */ + public String render(String templatePath, ImmutableMap dataModel) { + try { + Template template = configuration.getTemplate(templatePath); + StringWriter writer = new StringWriter(); + template.process(dataModel, writer); + return writer.toString(); + } catch (Exception e) { + throw new RuntimeException(String.format("Error rendering template %s", templatePath), e); + } + } +} diff --git a/common/src/test/java/google/registry/util/TemplateRendererTest.java b/common/src/test/java/google/registry/util/TemplateRendererTest.java new file mode 100644 index 000000000..1b523b214 --- /dev/null +++ b/common/src/test/java/google/registry/util/TemplateRendererTest.java @@ -0,0 +1,83 @@ +// Copyright 2026 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.util; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.google.common.collect.ImmutableMap; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link TemplateRenderer}. */ +class TemplateRendererTest { + + private final TemplateRenderer renderer = new TemplateRenderer(); + + @Test + void testRender_success() { + ImmutableMap data = + ImmutableMap.of( + "name", "World", "score", 42, "showMessage", true, "message", "Keep going!"); + String result = renderer.render("google/registry/util/test_template.ftl", data); + assertThat(result).isEqualTo("Hello World!\nYour score is 42.\nMessage: Keep going!\n"); + } + + @Test + void testRender_conditional_false() { + ImmutableMap data = + ImmutableMap.of("name", "User", "score", 0, "showMessage", false); + String result = renderer.render("google/registry/util/test_template.ftl", data); + assertThat(result).isEqualTo("Hello User!\nYour score is 0.\n"); + } + + @Test + void testRender_htmlEscaping() { + ImmutableMap data = + ImmutableMap.of("name", "World", "score", 42, "showMessage", false); + String result = renderer.render("google/registry/util/test_template.ftl", data); + assertThat(result).contains("Hello <b>World</b>!"); + } + + @Test + void testRender_missingTemplate_throwsException() { + assertThrows( + RuntimeException.class, + () -> renderer.render("non/existent/template.ftl", ImmutableMap.of())); + } + + @Test + void testRender_missingVariable_throwsException() { + // The template expects 'name', 'score', and 'showMessage', but the map is empty. + assertThrows( + RuntimeException.class, + () -> renderer.render("google/registry/util/test_template.ftl", ImmutableMap.of())); + } + + @Test + void testRender_unusedVariable_ignored() { + ImmutableMap data = + ImmutableMap.of( + "name", + "User", + "score", + 100, + "showMessage", + false, + "unusedKey", + "This should be ignored"); + String result = renderer.render("google/registry/util/test_template.ftl", data); + assertThat(result).isEqualTo("Hello User!\nYour score is 100.\n"); + } +} diff --git a/common/src/test/resources/google/registry/util/test_template.ftl b/common/src/test/resources/google/registry/util/test_template.ftl new file mode 100644 index 000000000..f871f4970 --- /dev/null +++ b/common/src/test/resources/google/registry/util/test_template.ftl @@ -0,0 +1,6 @@ +<#-- Copyright 2026 The Nomulus Authors. All Rights Reserved. --> +Hello ${name}! +Your score is ${score}. +<#if showMessage> +Message: ${message} + diff --git a/config/presubmits.py b/config/presubmits.py index 2b500ad34..50b731673 100644 --- a/config/presubmits.py +++ b/config/presubmits.py @@ -90,15 +90,15 @@ PRESUBMITS = { # License check PresubmitCheck( r".*Copyright 20\d{2} The Nomulus Authors\. All Rights Reserved\.", - ("java", "js", "soy", "sql", "py", "sh", "gradle", "ts"), { + ("java", "js", "sql", "py", "sh", "gradle", "ts", "ftl"), { ".git", "/build/", "node_modules/", "LoggerConfig.java", "registrar_bin.", "registrar_dbg.", "google-java-format-diff.py", - "nomulus.golden.sql", "soyutils_usegoog.js", "javascript/checks.js" + "nomulus.golden.sql", "javascript/checks.js" }, REQUIRED): "File did not include the license header.", # Files must end in a newline - PresubmitCheck(r".*\n$", ("java", "js", "soy", "sql", "py", "sh", "gradle", "ts", "xml"), + PresubmitCheck(r".*\n$", ("java", "js", "sql", "py", "sh", "gradle", "ts", "xml", "ftl"), {"node_modules/", ".idea"}, REQUIRED): "Source files must end in a newline.", @@ -127,33 +127,6 @@ PRESUBMITS = { "System.(out|err).println is only allowed in tools/ packages. Please " "use a logger instead.", - # Various Soy linting checks - PresubmitCheck( - r".* (/\*)?\* {?@param ", - "soy", - {}, - ): - "In SOY please use the ({@param name: string} /** User name. */) style" - " parameter passing instead of the ( * @param name User name.) style " - "parameter passing.", - PresubmitCheck( - r'.*\{[^}]+\w+:\s+"', - "soy", - {}, - ): - "Please don't use double-quoted string literals in Soy parameters", - PresubmitCheck( - r'.*autoescape\s*=\s*"[^s]', - "soy", - {}, - ): - "All soy templates must use strict autoescaping", - PresubmitCheck( - r".*noAutoescape", - "soy", - {}, - ): - "All soy templates must use strict autoescaping", PresubmitCheck( r".*\nimport\s+(?:static\s+)?.*\.shaded\..*", "java", diff --git a/core/build.gradle b/core/build.gradle index 3cb8cbaea..13849341e 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -94,7 +94,6 @@ processTestResources { configurations { jaxb - soy devtool nonprodImplementation.extendsFrom implementation @@ -120,7 +119,7 @@ configurations { // For reasons we do not understand, marking the following dependencies as // compileOnly instead of compile does not exclude them from runtimeClasspath. all { - // servlet-api:3.1 pulled in but not used by soy compiler + // servlet-api:3.1 pulled in but not used exclude group: 'javax.servlet', module: 'javax.servlet-api' } } @@ -181,7 +180,7 @@ dependencies { implementation deps['com.google.oauth-client:google-oauth-client-jetty'] implementation deps['com.google.oauth-client:google-oauth-client-servlet'] implementation deps['com.google.re2j:re2j'] - implementation deps['com.google.template:soy'] + implementation deps['org.freemarker:freemarker'] implementation deps['com.googlecode.json-simple:json-simple'] implementation deps['com.jcraft:jsch'] implementation deps['com.zaxxer:HikariCP'] @@ -299,9 +298,6 @@ dependencies { jaxb deps['org.glassfish.jaxb:jaxb-runtime'] jaxb deps['org.glassfish.jaxb:jaxb-xjc'] - // Dependency needed for soy to java compilation. - soy deps['com.google.template:soy'] - // Flyway classes needed to generate the golden file. implementation deps['org.flywaydb:flyway-core'] implementation deps['org.flywaydb:flyway-database-postgresql'] @@ -371,62 +367,7 @@ task jaxbToJava { } } -task soyToJava { - // Relative paths of soy directories. - def spec11SoyDir = "google/registry/reporting/spec11/soy" - def toolsSoyDir = "google/registry/tools/soy" - - def soyRelativeDirs = [spec11SoyDir, toolsSoyDir] - soyRelativeDirs.each { - inputs.dir "${resourcesSourceDir}/${it}" - outputs.dir "${generatedDir}/${it}" - } - - ext.soyToJava = { javaPackage, outputDirectory, soyFiles -> - project.services.get(ExecOperations).javaexec { - mainClass = "com.google.template.soy.SoyParseInfoGenerator" - classpath = configurations.soy - jvmArgs = ["--sun-misc-unsafe-memory-access=allow", "--enable-native-access=ALL-UNNAMED"] - args = ["--javaPackage", "${javaPackage}", - "--outputDirectory", "${outputDirectory}", - "--javaClassNameSource", "filename", - "--srcs", "${soyFiles.join(',')}"] - } - - // Replace the "@link" tags after the "@deprecated" tags in the generated - // files. The soy compiler doesn't generate imports for these, causing - // us to get warnings when we generate javadocs. - // TODO(b/200296387): To be fair, the deprecations are accurate: we're - // using the old "SoyInfo" classes instead of the new "Templates" files. - // When we convert to the new classes, this hack can go away. - def outputs = fileTree(outputDirectory) { - include '**/*.java' - } - - outputs.each { file -> - project.services.get(ExecOperations).exec { - commandLine = ['sed', '-i""', '-e', 's/@link/LINK/g', file.getCanonicalPath()] - } - } - } - - doLast { - soyToJava('google.registry.tools.soy', - "${generatedDir}/${toolsSoyDir}", - fileTree( - dir: "${resourcesSourceDir}/${toolsSoyDir}", - include: ['**/*.soy'])) - - soyToJava('google.registry.reporting.spec11.soy', - "${generatedDir}/${spec11SoyDir}", - fileTree( - dir: "${resourcesSourceDir}/${spec11SoyDir}", - include: ['**/*.soy'])) - } -} - compileJava.dependsOn jaxbToJava -compileJava.dependsOn soyToJava // Make testing artifacts available to be depended up on by other projects. // TODO: factor out google.registry.testing to be a separate project. diff --git a/core/gradle.lockfile b/core/gradle.lockfile index a86c04e04..af60e9f17 100644 --- a/core/gradle.lockfile +++ b/core/gradle.lockfile @@ -1,8 +1,7 @@ # This is a Gradle generated file for dependency locking. # Manual edits can break the build and are not advised. # This file is expected to be part of source control. -aopalliance:aopalliance:1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath -args4j:args4j:2.33=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath +args4j:args4j:2.33=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.charleskorn.kaml:kaml:0.20.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath com.fasterxml.jackson.core:jackson-annotations:2.21=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.fasterxml.jackson.core:jackson-core:2.20.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -81,13 +80,13 @@ com.google.api:gax-grpc:2.80.0=testCompileClasspath,testRuntimeClasspath com.google.api:gax-httpjson:2.74.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath com.google.api:gax-httpjson:2.80.0=testCompileClasspath,testRuntimeClasspath com.google.api:gax:2.74.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.apis:google-api-services-admin-directory:directory_v1-rev20260227-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.apis:google-api-services-admin-directory:directory_v1-rev20260421-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-bigquery:v2-rev20251012-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-cloudresourcemanager:v1-rev20250606-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.apis:google-api-services-dataflow:v1b3-rev20260405-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.apis:google-api-services-dataflow:v1b3-rev20260503-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-dns:v1-rev20260421-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-drive:v3-rev20260428-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.apis:google-api-services-gmail:v1-rev20260427-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.apis:google-api-services-gmail:v1-rev20260511-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-groupssettings:v1-rev20220614-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-healthcare:v1-rev20240130-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-iam:v2-rev20250502-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -138,33 +137,26 @@ com.google.cloud:google-cloud-tasks:2.51.0=compileClasspath,deploy_jar,nonprodCo com.google.cloud:grpc-gcp:1.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.cloud:libraries-bom:26.48.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.37.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath -com.google.code.gson:gson:2.10.1=soy +com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath com.google.code.gson:gson:2.13.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.common.html.types:types:1.0.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath com.google.dagger:dagger-compiler:2.59.2=annotationProcessor,testAnnotationProcessor com.google.dagger:dagger-spi:2.59.2=annotationProcessor,testAnnotationProcessor com.google.dagger:dagger:2.59.2=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath com.google.devtools.ksp:symbol-processing-api:2.2.20-2.0.3=annotationProcessor,testAnnotationProcessor com.google.errorprone:error_prone_annotation:2.48.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor -com.google.errorprone:error_prone_annotations:2.20.0=soy com.google.errorprone:error_prone_annotations:2.36.0=checkstyle com.google.errorprone:error_prone_annotations:2.48.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor com.google.errorprone:error_prone_annotations:2.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.errorprone:error_prone_check_api:2.48.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor com.google.errorprone:error_prone_core:2.48.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor -com.google.escapevelocity:escapevelocity:1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath com.google.flatbuffers:flatbuffers-java:24.3.25=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.flogger:flogger-system-backend:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath -com.google.flogger:flogger:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath -com.google.flogger:google-extensions:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath +com.google.flogger:flogger-system-backend:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.flogger:flogger:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.flogger:google-extensions:0.7.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.googlejavaformat:google-java-format:1.34.1=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor -com.google.guava:failureaccess:1.0.1=soy com.google.guava:failureaccess:1.0.3=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath -com.google.guava:guava-parent:32.1.1-jre=soy com.google.guava:guava-testlib:33.3.0-jre=testRuntimeClasspath com.google.guava:guava-testlib:33.6.0-jre=testCompileClasspath -com.google.guava:guava:32.1.1-jre=soy com.google.guava:guava:33.4.8-jre=checkstyle com.google.guava:guava:33.5.0-jre=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor com.google.guava:guava:33.6.0-jre=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -179,10 +171,8 @@ com.google.http-client:google-http-client-jackson2:1.46.3=compileClasspath,deplo com.google.http-client:google-http-client-jackson2:2.1.0=testCompileClasspath,testRuntimeClasspath com.google.http-client:google-http-client-protobuf:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.http-client:google-http-client:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.inject:guice:7.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath com.google.j2objc:j2objc-annotations:3.0.0=checkstyle com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath -com.google.jsinterop:jsinterop-annotations:1.0.1=soy com.google.jsinterop:jsinterop-annotations:2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.monitoring-client:contrib:1.0.7=testCompileClasspath,testRuntimeClasspath com.google.monitoring-client:metrics:1.0.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -195,15 +185,13 @@ com.google.oauth-client:google-oauth-client-servlet:1.36.0=deploy_jar,nonprodRun com.google.oauth-client:google-oauth-client-servlet:1.39.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath com.google.oauth-client:google-oauth-client:1.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.protobuf:protobuf-java-util:4.33.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath -com.google.protobuf:protobuf-java-util:4.35.0-RC2=testCompileClasspath,testRuntimeClasspath -com.google.protobuf:protobuf-java:3.21.7=soy +com.google.protobuf:protobuf-java-util:4.35.0=testCompileClasspath,testRuntimeClasspath com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor -com.google.protobuf:protobuf-java:4.35.0-RC2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java:4.35.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.re2j:re2j:1.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.template:soy:2024-02-26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath com.google.truth:truth:1.4.5=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.googlecode.json-simple:json-simple:1.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.ibm.icu:icu4j:73.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath +com.ibm.icu:icu4j:73.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.jcraft:jsch:0.1.55=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.lmax:disruptor:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.puppycrawl.tools:checkstyle:10.24.0=checkstyle @@ -252,7 +240,7 @@ io.apicurio:apicurio-registry-protobuf-schema-utilities:3.0.0.M2=compileClasspat io.github.classgraph:classgraph:4.8.162=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.16=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.github.java-diff-utils:java-diff-utils:4.17=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.github.ss-bhatt:testcontainers-valkey:1.0.0=testCompileClasspath,testRuntimeClasspath io.grpc:grpc-alts:1.76.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath io.grpc:grpc-alts:1.81.0=testCompileClasspath,testRuntimeClasspath @@ -351,7 +339,7 @@ jakarta-regexp:jakarta-regexp:1.4=compileClasspath,deploy_jar,nonprodCompileClas jakarta.activation:jakarta.activation-api:2.1.4=jaxb jakarta.activation:jakarta.activation-api:2.2.0-M1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.2.0-M2=compileClasspath,nonprodCompileClasspath,testCompileClasspath -jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath +jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath jakarta.mail:jakarta.mail-api:2.2.0-M1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.persistence:jakarta.persistence-api:3.2.0=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath jakarta.servlet:jakarta.servlet-api:6.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -360,8 +348,7 @@ jakarta.xml.bind:jakarta.xml.bind-api:4.0.4=deploy_jar,nonprodRuntimeClasspath,r jakarta.xml.bind:jakarta.xml.bind-api:4.0.5=jaxb jakarta.xml.bind:jakarta.xml.bind-api:4.1.0-M1=compileClasspath,nonprodCompileClasspath,testCompileClasspath javax.annotation:javax.annotation-api:1.3.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -javax.annotation:jsr250-api:1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath -javax.inject:javax.inject:1=annotationProcessor,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath +javax.inject:javax.inject:1=annotationProcessor,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath javax.jdo:jdo2-api:2.3-20090302111651=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath javax.validation:validation-api:1.0.0.GA=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath joda-time:joda-time:2.14.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -438,10 +425,9 @@ org.bouncycastle:bcpg-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileCla org.bouncycastle:bcpkix-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.bouncycastle:bcprov-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.bouncycastle:bcutil-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.checkerframework:checker-compat-qual:2.5.3=annotationProcessor,compileClasspath,nonprodCompileClasspath,soy,testAnnotationProcessor,testCompileClasspath +org.checkerframework:checker-compat-qual:2.5.3=annotationProcessor,compileClasspath,nonprodCompileClasspath,testAnnotationProcessor,testCompileClasspath org.checkerframework:checker-compat-qual:2.5.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.checkerframework:checker-qual:3.19.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor -org.checkerframework:checker-qual:3.33.0=soy org.checkerframework:checker-qual:3.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.checkerframework:checker-qual:3.49.3=checkstyle org.codehaus.mojo:animal-sniffer-annotations:1.24=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath @@ -465,8 +451,9 @@ org.eclipse.jetty:jetty-server:12.1.9=testCompileClasspath,testRuntimeClasspath org.eclipse.jetty:jetty-session:12.1.9=testCompileClasspath,testRuntimeClasspath org.eclipse.jetty:jetty-util:12.1.9=testCompileClasspath,testRuntimeClasspath org.eclipse.jetty:jetty-xml:12.1.9=testCompileClasspath,testRuntimeClasspath -org.flywaydb:flyway-core:12.6.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.flywaydb:flyway-database-postgresql:12.6.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.flywaydb:flyway-core:12.6.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.flywaydb:flyway-database-postgresql:12.6.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.freemarker:freemarker:2.3.34=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.glassfish.jaxb:codemodel:4.0.8=jaxb org.glassfish.jaxb:jaxb-core:4.0.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.glassfish.jaxb:jaxb-core:4.0.8=jaxb @@ -514,7 +501,6 @@ org.jetbrains:annotations:13.0=annotationProcessor,testAnnotationProcessor org.jetbrains:annotations:17.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jline:jline:3.30.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.joda:joda-money:2.0.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.json:json:20230618=soy org.json:json:20251224=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jsoup:jsoup:1.22.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jspecify:jspecify:1.0.0=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath @@ -535,17 +521,12 @@ org.mockito:mockito-junit-jupiter:5.23.0=testCompileClasspath,testRuntimeClasspa org.objenesis:objenesis:3.3=testRuntimeClasspath org.ogce:xpp3:1.1.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-analysis:9.5=soy org.ow2.asm:asm-analysis:9.7.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.5=soy org.ow2.asm:asm-commons:9.7.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.ow2.asm:asm-commons:9.9=jacocoAnt -org.ow2.asm:asm-tree:9.5=soy org.ow2.asm:asm-tree:9.7.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.ow2.asm:asm-tree:9.9=jacocoAnt -org.ow2.asm:asm-util:9.5=soy org.ow2.asm:asm-util:9.7.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm:9.5=soy org.ow2.asm:asm:9.7.1=compileClasspath,nonprodCompileClasspath org.ow2.asm:asm:9.8=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.ow2.asm:asm:9.9=jacocoAnt diff --git a/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java b/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java index 3a1d010c8..935ee3de6 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java +++ b/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java @@ -43,8 +43,8 @@ import static google.registry.util.CollectionUtils.nullToEmpty; import static google.registry.util.DateTimeUtils.END_INSTANT; import static google.registry.util.DateTimeUtils.isAtOrAfter; import static google.registry.util.DateTimeUtils.minusDays; +import static google.registry.util.DateTimeUtils.plusYears; import static google.registry.util.DomainNameUtils.ACE_PREFIX; -import static java.time.ZoneOffset.UTC; import static java.util.stream.Collectors.joining; import com.google.common.base.CharMatcher; @@ -154,7 +154,7 @@ public class DomainFlowUtils { /** Warning message for allocation of collision domains in sunrise. */ public static final String COLLISION_MESSAGE = "Domain on the name collision list was allocated. But by policy, the domain will not be " - + "delegated. Please visit https://www.icann.org/namecollision for more information on " + + "delegated. Please visit https://www.icann.org/namecollision for more information on " + "name collision."; /** Strict validator for ascii lowercase letters, digits, and "-", allowing "." as a separator */ @@ -581,13 +581,12 @@ public class DomainFlowUtils { InternetDomainName domainName, Optional domain, @Nullable CurrencyUnit topLevelCurrency, - Instant currentDate, + Instant now, DomainPricingLogic pricingLogic, Optional allocationToken, boolean isAvailable, @Nullable BillingRecurrence billingRecurrence) throws EppException { - Instant now = currentDate; // Use the custom effective date specified in the fee check request, if there is one. if (feeRequest.getEffectiveDate().isPresent()) { now = feeRequest.getEffectiveDate().get(); @@ -816,7 +815,7 @@ public class DomainFlowUtils { return fee.getType(); } ImmutableList types = fee.parseDescriptionForTypes(); - if (types.size() == 0) { + if (types.isEmpty()) { throw new FeeDescriptionParseException(fee.getDescription()); } else if (types.size() > 1) { throw new FeeDescriptionMultipleMatchesException(fee.getDescription(), types); @@ -848,7 +847,7 @@ public class DomainFlowUtils { */ public static void validateRegistrationPeriod(Instant now, Instant newExpirationTime) throws EppException { - if (now.atZone(UTC).plusYears(MAX_REGISTRATION_YEARS).toInstant().isBefore(newExpirationTime)) { + if (plusYears(now, MAX_REGISTRATION_YEARS).isBefore(newExpirationTime)) { throw new ExceedsMaxRegistrationYearsException(); } } @@ -907,7 +906,7 @@ public class DomainFlowUtils { return ImmutableSet.copyOf(union(difference(oldDsData, toRemove), toAdd)); } - /** If a domain "clientUpdateProhibited" set, updates must clear it or fail. */ + /** If a domain has "clientUpdateProhibited" set, updates must clear it or fail. */ static void verifyClientUpdateNotProhibited(Update command, Domain existingResource) throws ResourceHasClientUpdateProhibitedException { if (existingResource.getStatusValues().contains(StatusValue.CLIENT_UPDATE_PROHIBITED) @@ -996,7 +995,13 @@ public class DomainFlowUtils { } } - /** Check that the claims period hasn't ended. */ + /** + * Check that the claims period hasn't ended. + * + * @param tld the {@link Tld} to check + * @param now the current {@link Instant} + * @throws ClaimsPeriodEndedException if the claims period has ended + */ static void verifyClaimsPeriodNotEnded(Tld tld, Instant now) throws ClaimsPeriodEndedException { if (!now.isBefore(tld.getClaimsPeriodEnd())) { throw new ClaimsPeriodEndedException(tld.getTldStr()); @@ -1008,6 +1013,9 @@ public class DomainFlowUtils { * *

{@link BigDecimal} has a concept of significant figures, so zero is not always zero. E.g. * zero in USD is 0.00, whereas zero in Yen is 0, and zero in Dinars is 0.000 (!). + * + * @param currencyUnit the {@link CurrencyUnit} + * @return zero in the given currency */ static BigDecimal zeroInCurrency(CurrencyUnit currencyUnit) { return Money.of(currencyUnit, BigDecimal.ZERO).getAmount(); @@ -1016,6 +1024,12 @@ public class DomainFlowUtils { /** * Check that if there's a claims notice it's on the claims list, and that if there's not one it's * not on the claims list. + * + * @param domainName the {@link InternetDomainName} to check + * @param claimsList the current {@link ClaimsList} + * @param hasSignedMarks whether signed marks are present + * @param hasClaimsNotice whether a claims notice is present + * @throws EppException if the claims notice status is incorrect */ static void verifyClaimsNoticeIfAndOnlyIfNeeded( InternetDomainName domainName, @@ -1032,7 +1046,12 @@ public class DomainFlowUtils { } } - /** Check that there are no code marks, which is a type of mark we don't support. */ + /** + * Check that there are no code marks, which is a type of mark we don't support. + * + * @param launchCreate the {@link LaunchCreateExtension} + * @throws UnsupportedMarkTypeException if code marks are present + */ static void verifyNoCodeMarks(LaunchCreateExtension launchCreate) throws UnsupportedMarkTypeException { if (launchCreate.hasCodeMarks()) { @@ -1040,7 +1059,13 @@ public class DomainFlowUtils { } } - /** Create a response extension listing the fees on a domain create. */ + /** + * Create a response extension listing the fees on a domain create. + * + * @param feeCreate the {@link FeeTransformCommandExtension} + * @param feesAndCredits the {@link FeesAndCredits} + * @return the {@link FeeTransformResponseExtension} + */ static FeeTransformResponseExtension createFeeCreateResponse( FeeTransformCommandExtension feeCreate, FeesAndCredits feesAndCredits) { return feeCreate @@ -1058,10 +1083,21 @@ public class DomainFlowUtils { * their flow. For example, if a grace period delete occurs, we must add -1 counters for the * associated NET_ADDS_#_YRS field, if it exists. * - *

The steps are as follows: 1. Find all HistoryEntries under the domain modified in the past, - * up to the maxSearchPeriod. 2. Only keep HistoryEntries with a DomainTransactionRecord that a) - * hasn't been reported yet and b) matches the predicate 3. Return the transactionRecords under - * the most recent HistoryEntry that fits the above criteria, with negated reportAmounts. + *

The steps are as follows: + * + *

    + *
  1. Find all HistoryEntries under the domain modified in the past, up to the maxSearchPeriod. + *
  2. Only keep HistoryEntries with a DomainTransactionRecord that a) hasn't been reported yet + * and b) matches the predicate + *
  3. Return the transactionRecords under the most recent HistoryEntry that fits the above + * criteria, with negated reportAmounts. + *
+ * + * @param domain the {@link Domain} to create records for + * @param now the current {@link Instant} + * @param maxSearchPeriod the {@link Duration} to search back + * @param cancelableFields the set of {@link TransactionReportField}s that can be canceled + * @return the set of canceling {@link DomainTransactionRecord}s */ public static ImmutableSet createCancelingRecords( Domain domain, @@ -1225,13 +1261,6 @@ public class DomainFlowUtils { } } - /** Having a registrant is prohibited by registry policy. */ - public static class RegistrantProhibitedException extends ParameterValuePolicyErrorException { - public RegistrantProhibitedException() { - super("Having a registrant is prohibited by registry policy"); - } - } - /** Too many nameservers set on this domain. */ static class TooManyNameserversException extends ParameterValuePolicyErrorException { public TooManyNameserversException(String message) { @@ -1384,6 +1413,13 @@ public class DomainFlowUtils { } } + /** Having a registrant is prohibited by registry policy. */ + public static class RegistrantProhibitedException extends ParameterValuePolicyErrorException { + public RegistrantProhibitedException() { + super("Having a registrant is prohibited by registry policy"); + } + } + /** The fee description passed in the transform command cannot be parsed. */ public static class FeeDescriptionParseException extends ParameterValuePolicyErrorException { public FeeDescriptionParseException(String description) { diff --git a/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java b/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java index f561d2e6a..780de3602 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java @@ -34,8 +34,8 @@ import static google.registry.flows.domain.token.AllocationTokenFlowUtils.maybeA import static google.registry.flows.domain.token.AllocationTokenFlowUtils.verifyBulkTokenAllowedOnDomain; import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_RENEW; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; +import static google.registry.util.DateTimeUtils.plusYears; import static google.registry.util.DateTimeUtils.toLocalDate; -import static java.time.ZoneOffset.UTC; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -192,11 +192,7 @@ public final class DomainRenewFlow implements MutatingFlow { existingDomain = maybeApplyBulkPricingRemovalToken(existingDomain, allocationToken); Instant newExpirationTime = - existingDomain - .getRegistrationExpirationTime() - .atZone(UTC) - .plusYears(years) - .toInstant(); // Uncapped + plusYears(existingDomain.getRegistrationExpirationTime(), years); // Uncapped validateRegistrationPeriod(now, newExpirationTime); Optional feeRenew = eppInput.getSingleExtension(FeeRenewCommandExtension.class); diff --git a/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java b/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java index 92d65f864..adb13b925 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java @@ -14,7 +14,6 @@ package google.registry.flows.domain; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet; import static com.google.common.collect.Sets.symmetricDifference; import static com.google.common.collect.Sets.union; @@ -58,17 +57,17 @@ import google.registry.flows.custom.DomainUpdateFlowCustomLogic.BeforeSaveParame import google.registry.flows.custom.EntityChanges; import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedForTldWithNameserverAllowListException; import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException; +import google.registry.flows.exceptions.ContactsProhibitedException; import google.registry.model.ImmutableObject; import google.registry.model.billing.BillingBase.Reason; import google.registry.model.billing.BillingEvent; import google.registry.model.domain.Domain; import google.registry.model.domain.DomainCommand.Update; -import google.registry.model.domain.DomainCommand.Update.AddRemove; import google.registry.model.domain.DomainCommand.Update.Change; +import google.registry.model.domain.DomainCommand.Update.DomainAddRemove; import google.registry.model.domain.DomainHistory; import google.registry.model.domain.fee.FeeUpdateCommandExtension; import google.registry.model.domain.metadata.MetadataExtension; -import google.registry.model.domain.secdns.DomainDsData; import google.registry.model.domain.secdns.SecDnsUpdateExtension; import google.registry.model.domain.secdns.SecDnsUpdateExtension.Add; import google.registry.model.domain.secdns.SecDnsUpdateExtension.Remove; @@ -118,6 +117,7 @@ import java.util.Optional; * @error {@link NameserversNotSpecifiedForTldWithNameserverAllowListException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException} * @error {@link RegistrantProhibitedException} + * @error {@link ContactsProhibitedException} * @error {@link DomainFlowUtils.SecDnsAllUsageException} * @error {@link DomainFlowUtils.TooManyDsRecordsException} * @error {@link DomainFlowUtils.TooManyNameserversException} @@ -214,8 +214,8 @@ public final class DomainUpdateFlow implements MutatingFlow { private void verifyUpdateAllowed(Update command, Domain existingDomain, Instant now) throws EppException { verifyOptionalAuthInfo(authInfo, existingDomain); - AddRemove add = command.getInnerAdd(); - AddRemove remove = command.getInnerRemove(); + DomainAddRemove add = command.getInnerAdd(); + DomainAddRemove remove = command.getInnerRemove(); String tldStr = existingDomain.getTld(); if (!isSuperuser) { verifyNoDisallowedStatuses(existingDomain, UPDATE_DISALLOWED_STATUSES); @@ -234,8 +234,8 @@ public final class DomainUpdateFlow implements MutatingFlow { } private Domain performUpdate(Update command, Domain domain, Instant now) throws EppException { - AddRemove add = command.getInnerAdd(); - AddRemove remove = command.getInnerRemove(); + DomainAddRemove add = command.getInnerAdd(); + DomainAddRemove remove = command.getInnerRemove(); Optional secDnsUpdate = eppInput.getSingleExtension(SecDnsUpdateExtension.class); verifyAddsAndRemoves(domain.getNameservers(), add.getNameservers(), remove.getNameservers()); @@ -251,28 +251,29 @@ public final class DomainUpdateFlow implements MutatingFlow { Domain.Builder domainBuilder = domain .asBuilder() - // Handle the secDNS extension. As dsData in secDnsUpdate is read from EPP input and - // does not have domainRepoId set, we create a copy of the existing dsData without - // domainRepoId for comparison. + // Handle the secDNS extension. .setDsData( secDnsUpdate.isPresent() - ? updateDsData( - domain.getDsData().stream() - .map(DomainDsData::cloneWithoutDomainRepoId) - .collect(toImmutableSet()), - secDnsUpdate.get()) + ? updateDsData(domain.getDsData(), secDnsUpdate.get()) : domain.getDsData()) .setLastEppUpdateTime(now) - .setLastEppUpdateRegistrarId(registrarId) - .addStatusValues(add.getStatusValues()) - .removeStatusValues(remove.getStatusValues()) - .setAuthInfo(Optional.ofNullable(change.getAuthInfo()).orElse(domain.getAuthInfo())); + .setLastEppUpdateRegistrarId(registrarId); + + if (!add.getStatusValues().isEmpty()) { + domainBuilder.addStatusValues(add.getStatusValues()); + } + if (!remove.getStatusValues().isEmpty()) { + domainBuilder.removeStatusValues(remove.getStatusValues()); + } + + domainBuilder.setAuthInfo( + Optional.ofNullable(change.getAuthInfo()).orElse(domain.getAuthInfo())); if (!add.getNameservers().isEmpty()) { - domainBuilder.addNameservers(add.getNameservers().stream().collect(toImmutableSet())); + domainBuilder.addNameservers(add.getNameservers()); } if (!remove.getNameservers().isEmpty()) { - domainBuilder.removeNameservers(remove.getNameservers().stream().collect(toImmutableSet())); + domainBuilder.removeNameservers(remove.getNameservers()); } Optional superuserExt = diff --git a/core/src/main/java/google/registry/flows/host/HostUpdateFlow.java b/core/src/main/java/google/registry/flows/host/HostUpdateFlow.java index 59171dc4c..c5a2c243f 100644 --- a/core/src/main/java/google/registry/flows/host/HostUpdateFlow.java +++ b/core/src/main/java/google/registry/flows/host/HostUpdateFlow.java @@ -36,7 +36,6 @@ import static google.registry.util.CollectionUtils.isNullOrEmpty; import com.google.cloud.tasks.v2.Task; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; -import google.registry.batch.AsyncTaskEnqueuer; import google.registry.batch.CloudTasksUtils; import google.registry.dns.RefreshDnsOnHostRenameAction; import google.registry.flows.EppException; @@ -59,8 +58,8 @@ import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.EppResponse; import google.registry.model.host.Host; import google.registry.model.host.HostCommand.Update; -import google.registry.model.host.HostCommand.Update.AddRemove; import google.registry.model.host.HostCommand.Update.Change; +import google.registry.model.host.HostCommand.Update.HostAddRemove; import google.registry.model.host.HostHistory; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.persistence.VKey; @@ -122,7 +121,6 @@ public final class HostUpdateFlow implements MutatingFlow { @Inject @TargetId String targetId; @Inject @Superuser boolean isSuperuser; @Inject HostHistory.Builder historyBuilder; - @Inject AsyncTaskEnqueuer asyncTaskEnqueuer; @Inject EppResponse.Builder responseBuilder; @Inject CloudTasksUtils cloudTasksUtils; @@ -148,6 +146,7 @@ public final class HostUpdateFlow implements MutatingFlow { ? tm().loadByKey(existingHost.getSuperordinateDomain()).cloneProjectedAtTime(now) : null; // Note that lookupSuperordinateDomain calls cloneProjectedAtTime on the domain for us. + Optional newSuperordinateDomain = lookupSuperordinateDomain(validateHostName(newHostName), now); verifySuperordinateDomainNotInPendingDelete(newSuperordinateDomain.orElse(null)); @@ -157,8 +156,8 @@ public final class HostUpdateFlow implements MutatingFlow { if (isHostRename && ForeignKeyUtils.loadKey(Host.class, newHostName, now).isPresent()) { throw new HostAlreadyExistsException(newHostName); } - AddRemove add = command.getInnerAdd(); - AddRemove remove = command.getInnerRemove(); + HostAddRemove add = command.getInnerAdd(); + HostAddRemove remove = command.getInnerRemove(); verifyAddsAndRemoves( existingHost.getStatusValues(), add.getStatusValues(), remove.getStatusValues()); verifyAddsAndRemoves( diff --git a/core/src/main/java/google/registry/model/ImmutableObject.java b/core/src/main/java/google/registry/model/ImmutableObject.java index 576144ff2..fe4775c6d 100644 --- a/core/src/main/java/google/registry/model/ImmutableObject.java +++ b/core/src/main/java/google/registry/model/ImmutableObject.java @@ -106,19 +106,24 @@ public abstract class ImmutableObject implements Cloneable { return hashCode; } - /** Returns a clone of the given object. */ - @SuppressWarnings("unchecked") - protected static T clone(T t) { + @Override + @SuppressWarnings("AmbiguousMethodReference") + public ImmutableObject clone() { try { - T clone = (T) t.clone(); - // Clear the hashCode since we often mutate clones before handing them out. + ImmutableObject clone = (ImmutableObject) super.clone(); clone.hashCode = null; return clone; - } catch (CloneNotSupportedException e) { // Yes it is. - throw new IllegalStateException(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(); } } + /** Returns a clone of the given object. */ + @SuppressWarnings({"unchecked", "AmbiguousMethodReference"}) + protected static T clone(T t) { + return (T) t.clone(); + } + /** Returns a clone of the given object with empty fields set to null. */ protected static T cloneEmptyToNull(T t) { return ModelUtils.cloneEmptyToNull(t); @@ -233,7 +238,7 @@ public abstract class ImmutableObject implements Cloneable { } } - /** Marker to indicate that this filed should be ignored by {@link #toDiffableFieldMap}. */ + /** Marker to indicate that this field should be ignored by {@link #toDiffableFieldMap}. */ @Documented @Retention(RUNTIME) @Target(FIELD) diff --git a/core/src/main/java/google/registry/model/domain/DomainCommand.java b/core/src/main/java/google/registry/model/domain/DomainCommand.java index d85235b62..9f59502ca 100644 --- a/core/src/main/java/google/registry/model/domain/DomainCommand.java +++ b/core/src/main/java/google/registry/model/domain/DomainCommand.java @@ -17,7 +17,6 @@ package google.registry.model.domain; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Sets.difference; -import static google.registry.util.CollectionUtils.difference; import static google.registry.util.CollectionUtils.isNullOrEmpty; import static google.registry.util.CollectionUtils.nullSafeImmutableCopy; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; @@ -28,8 +27,11 @@ import com.google.common.collect.ImmutableSet; import google.registry.flows.EppException.ParameterValuePolicyErrorException; import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException; import google.registry.flows.exceptions.ContactsProhibitedException; +import google.registry.model.Buildable; import google.registry.model.ForeignKeyUtils; import google.registry.model.ImmutableObject; +import google.registry.model.eppcommon.AuthInfo; +import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppinput.ResourceCommand.AbstractSingleResourceCommand; import google.registry.model.eppinput.ResourceCommand.ResourceCheck; import google.registry.model.eppinput.ResourceCommand.ResourceCreateOrChange; @@ -37,6 +39,8 @@ import google.registry.model.eppinput.ResourceCommand.ResourceUpdate; import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand; import google.registry.model.host.Host; import google.registry.persistence.VKey; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAttribute; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlElementWrapper; @@ -68,10 +72,10 @@ public class DomainCommand { throws InvalidReferencesException, ParameterValuePolicyErrorException; } - /** The fields on "chgType" from RFC5731. */ + /** The fields on "chgType" from RFC5731. */ @XmlTransient - public static class DomainCreateOrChange extends ImmutableObject - implements ResourceCreateOrChange { + public abstract static class DomainCreateOrChange + extends ImmutableObject implements ResourceCreateOrChange { /** The contactId of the registrant who registered this domain. */ @XmlElement(name = "registrant") @@ -92,9 +96,10 @@ public class DomainCommand { /** * A create command for a {@link Domain}, mapping "createType" from RFC5731. + * href="https://tools.ietf.org/html/rfc5731">RFC5731. */ @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) @XmlType( propOrder = { "domainName", @@ -147,17 +152,12 @@ public class DomainCommand { return nullToEmptyImmutableCopy(nameservers); } - @Override - public DomainAuthInfo getAuthInfo() { - return authInfo; - } - /** Creates a copy of this {@link Create} with hard links to hosts and contacts. */ @Override public Create cloneAndLinkReferences(Instant now) throws InvalidReferencesException, ParameterValuePolicyErrorException { Create clone = clone(this); - clone.nameservers = linkHosts(clone.nameserverHostNames, now); + clone.nameservers = linkHosts(nullSafeImmutableCopy(clone.nameserverHostNames), now); if (registrantContactId != null) { throw new RegistrantProhibitedException(); } @@ -166,14 +166,65 @@ public class DomainCommand { } return clone; } + + /** Builder for {@link Create}. */ + public static class Builder extends Buildable.Builder { + public Builder setDomainName(String domainName) { + getInstance().domainName = domainName; + return this; + } + + public Builder setPeriod(Period period) { + getInstance().period = period; + return this; + } + + public Builder setNameserverHostNames(ImmutableSet nameserverHostNames) { + getInstance().nameserverHostNames = + isNullOrEmpty(nameserverHostNames) ? null : nameserverHostNames; + return this; + } + + public Builder setForeignKeyedDesignatedContacts( + ImmutableSet foreignKeyedDesignatedContacts) { + getInstance().foreignKeyedDesignatedContacts = + isNullOrEmpty(foreignKeyedDesignatedContacts) ? null : foreignKeyedDesignatedContacts; + return this; + } + + public Builder setRegistrant(String registrant) { + getInstance().registrantContactId = registrant; + return this; + } + + public Builder setAuthInfo(DomainAuthInfo authInfo) { + getInstance().authInfo = authInfo; + return this; + } + } } /** A delete command for a {@link Domain}. */ @XmlRootElement - public static class Delete extends AbstractSingleResourceCommand {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Delete extends AbstractSingleResourceCommand { + @XmlElement(name = "name") + String name; + + @Override + public String getTargetId() { + return name; + } + + @Override + public void setTargetId(String targetId) { + this.name = targetId; + } + } /** An info request for a {@link Domain}. */ @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) public static class Info extends ImmutableObject implements SingleResourceCommand { /** The name of the domain to look up, and an attribute specifying the host lookup type. */ @@ -226,7 +277,7 @@ public class DomainCommand { } @Override - public DomainAuthInfo getAuthInfo() { + public AuthInfo getAuthInfo() { return authInfo; } } @@ -237,12 +288,27 @@ public class DomainCommand { /** A renew command for a {@link Domain}. */ @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"name", "currentExpirationDate", "period"}) public static class Renew extends AbstractSingleResourceCommand { + @XmlElement(name = "name") + String name; + + @Override + public String getTargetId() { + return name; + } + + @Override + public void setTargetId(String targetId) { + this.name = targetId; + } + @XmlElement(name = "curExpDate") LocalDate currentExpirationDate; /** The period that this domain's state was set to last for. */ - Period period; + @XmlElement Period period; public LocalDate getCurrentExpirationDate() { return currentExpirationDate; @@ -251,13 +317,46 @@ public class DomainCommand { public Period getPeriod() { return firstNonNull(period, DEFAULT_PERIOD); } + + /** Builder for {@link Renew}. */ + public static class Builder extends Buildable.Builder { + public Builder setTargetId(String targetId) { + getInstance().setTargetId(targetId); + return this; + } + + public Builder setCurrentExpirationDate(LocalDate currentExpirationDate) { + getInstance().currentExpirationDate = currentExpirationDate; + return this; + } + + public Builder setPeriod(Period period) { + getInstance().period = period; + return this; + } + } } /** A transfer operation for a {@link Domain}. */ @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"name", "period", "authInfo"}) public static class Transfer extends AbstractSingleResourceCommand { + @XmlElement(name = "name") + String name; + + @Override + public String getTargetId() { + return name; + } + + @Override + public void setTargetId(String targetId) { + this.name = targetId; + } + /** The period to extend this domain's registration upon completion of the transfer. */ - Period period; + @XmlElement Period period; /** Authorization info used to validate if client has permissions to perform this operation. */ DomainAuthInfo authInfo; @@ -267,25 +366,40 @@ public class DomainCommand { } @Override - public DomainAuthInfo getAuthInfo() { + public AuthInfo getAuthInfo() { return authInfo; } } /** An update to a {@link Domain}. */ @XmlRootElement - @XmlType(propOrder = {"targetId", "innerAdd", "innerRemove", "innerChange"}) - public static class Update extends ResourceUpdate + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"name", "innerAdd", "innerRemove", "innerChange"}) + public static class Update + extends ResourceUpdate implements CreateOrUpdate { + @XmlElement(name = "name") + String name; + + @Override + public String getTargetId() { + return name; + } + + @Override + public void setTargetId(String targetId) { + this.name = targetId; + } + @XmlElement(name = "chg") protected Change innerChange; @XmlElement(name = "add") - protected AddRemove innerAdd; + protected DomainAddRemove innerAdd; @XmlElement(name = "rem") - protected AddRemove innerRemove; + protected DomainAddRemove innerRemove; @Override protected Change getNullableInnerChange() { @@ -293,25 +407,49 @@ public class DomainCommand { } @Override - protected AddRemove getNullableInnerAdd() { + protected DomainAddRemove getNullableInnerAdd() { return innerAdd; } @Override - protected AddRemove getNullableInnerRemove() { + protected DomainAddRemove getNullableInnerRemove() { return innerRemove; } public boolean noChangesPresent() { - AddRemove emptyAddRemove = new AddRemove(); + DomainAddRemove emptyAddRemove = new DomainAddRemove(); return emptyAddRemove.equals(getInnerAdd()) && emptyAddRemove.equals(getInnerRemove()) && new Change().equals(getInnerChange()); } + /** Builder for {@link Update}. */ + public static class Builder extends Buildable.Builder { + public Builder setTargetId(String targetId) { + getInstance().setTargetId(targetId); + return this; + } + + public Builder setInnerAdd(DomainAddRemove innerAdd) { + getInstance().innerAdd = innerAdd; + return this; + } + + public Builder setInnerRemove(DomainAddRemove innerRemove) { + getInstance().innerRemove = innerRemove; + return this; + } + + public Builder setInnerChange(Change innerChange) { + getInstance().innerChange = innerChange; + return this; + } + } + /** The inner change type on a domain update command. */ + @XmlAccessorType(XmlAccessType.FIELD) @XmlType(propOrder = {"nameserverHostNames", "foreignKeyedDesignatedContacts", "statusValues"}) - public static class AddRemove extends ResourceUpdate.AddRemove { + public static class DomainAddRemove extends ResourceUpdate.AddRemove { /** Fully qualified host names of the hosts that are the nameservers for the domain. */ @XmlElementWrapper(name = "ns") @XmlElement(name = "hostObj") @@ -324,6 +462,25 @@ public class DomainCommand { @XmlElement(name = "contact") Set foreignKeyedDesignatedContacts; + @XmlElement(name = "status") + Set statusValues; + + public boolean isEmpty() { + return isNullOrEmpty(nameserverHostNames) + && isNullOrEmpty(foreignKeyedDesignatedContacts) + && isNullOrEmpty(statusValues); + } + + @Override + public void setStatusValues(ImmutableSet statusValues) { + this.statusValues = statusValues; + } + + @Override + public ImmutableSet getStatusValues() { + return nullToEmptyImmutableCopy(statusValues); + } + public ImmutableSet getNameserverHostNames() { return nullSafeImmutableCopy(nameserverHostNames); } @@ -332,11 +489,25 @@ public class DomainCommand { return nullToEmptyImmutableCopy(nameservers); } - /** Creates a copy of this {@link AddRemove} with hard links to hosts and contacts. */ - private AddRemove cloneAndLinkReferences(Instant now) + /** Builder for {@link DomainAddRemove}. */ + public static class Builder extends Buildable.Builder { + public Builder setNameserverHostNames(ImmutableSet nameserverHostNames) { + getInstance().nameserverHostNames = + isNullOrEmpty(nameserverHostNames) ? null : nameserverHostNames; + return this; + } + + public Builder setStatusValues(ImmutableSet statusValues) { + getInstance().statusValues = isNullOrEmpty(statusValues) ? null : statusValues; + return this; + } + } + + /** Creates a copy of this {@link DomainAddRemove} with hard links to hosts and contacts. */ + private DomainAddRemove cloneAndLinkReferences(Instant now) throws InvalidReferencesException, ContactsProhibitedException { - AddRemove clone = clone(this); - clone.nameservers = linkHosts(clone.nameserverHostNames, now); + DomainAddRemove clone = clone(this); + clone.nameservers = linkHosts(nullSafeImmutableCopy(clone.nameserverHostNames), now); if (!isNullOrEmpty(foreignKeyedDesignatedContacts)) { throw new ContactsProhibitedException(); } @@ -345,8 +516,17 @@ public class DomainCommand { } /** The inner change type on a domain update command. */ + @XmlAccessorType(XmlAccessType.FIELD) @XmlType(propOrder = {"registrantContactId", "authInfo"}) public static class Change extends DomainCreateOrChange { + /** Builder for {@link Change}. */ + public static class Builder extends Buildable.Builder { + public Builder setAuthInfo(DomainAuthInfo authInfo) { + getInstance().authInfo = authInfo; + return this; + } + } + Change cloneAndLinkReferences() throws RegistrantProhibitedException { Change clone = clone(this); if (clone.registrantContactId != null) { @@ -373,7 +553,7 @@ public class DomainCommand { } } - private static Set> linkHosts(Set hostNames, Instant now) + private static ImmutableSet> linkHosts(ImmutableSet hostNames, Instant now) throws InvalidReferencesException { if (hostNames == null) { return null; @@ -383,7 +563,7 @@ public class DomainCommand { /** Loads host keys to cached EPP resources by their foreign keys. */ private static ImmutableMap> loadByForeignKeysCached( - Set foreignKeys, Instant now) throws InvalidReferencesException { + ImmutableSet foreignKeys, Instant now) throws InvalidReferencesException { ImmutableMap> fks = ForeignKeyUtils.loadKeysByCacheIfEnabled(Host.class, foreignKeys, now); if (!fks.keySet().equals(foreignKeys)) { @@ -394,14 +574,14 @@ public class DomainCommand { } /** Exception to throw when referenced objects don't exist. */ - public static class InvalidReferencesException extends Exception { + public static class InvalidReferencesException extends ParameterValuePolicyErrorException { private final ImmutableSet foreignKeys; private final Class type; - InvalidReferencesException(Class type, ImmutableSet foreignKeys) { + public InvalidReferencesException(Class type, Set foreignKeys) { super(String.format("Invalid %s reference IDs: %s", type.getSimpleName(), foreignKeys)); this.type = checkNotNull(type); - this.foreignKeys = foreignKeys; + this.foreignKeys = nullToEmptyImmutableCopy(foreignKeys); } public ImmutableSet getForeignKeys() { diff --git a/core/src/main/java/google/registry/model/domain/fee/Fee.java b/core/src/main/java/google/registry/model/domain/fee/Fee.java index cefa86fcc..a5ce86241 100644 --- a/core/src/main/java/google/registry/model/domain/fee/Fee.java +++ b/core/src/main/java/google/registry/model/domain/fee/Fee.java @@ -20,16 +20,22 @@ import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Range; +import google.registry.model.Buildable; import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension; import java.math.BigDecimal; import java.time.Instant; +import java.time.Period; /** - * A fee, in currency units specified elsewhere in the xml, with type of the fee an optional fee - * description. + * A fee, in currency units specified elsewhere in the XML, with a type and an optional description. */ public class Fee extends BaseFee { + @Override + public Fee clone() { + return (Fee) super.clone(); + } + /** Creates a Fee for the given cost and type with the default description. */ public static Fee create( BigDecimal cost, FeeType type, boolean isPremium, Object... descriptionArgs) { @@ -55,7 +61,7 @@ public class Fee extends BaseFee { BigDecimal cost, FeeType type, boolean isPremium, String description) { Fee instance = new Fee(); instance.cost = checkNotNull(cost); - checkArgument(instance.cost.signum() >= 0, "Cost must be a positive number"); + checkArgument(instance.cost.signum() >= 0, "Cost must be a non-negative number"); instance.type = checkNotNull(type); instance.isPremium = isPremium; instance.description = description; @@ -68,4 +74,38 @@ public class Fee extends BaseFee { ServiceExtension.FEE_0_12.getUri(), ServiceExtension.FEE_0_11.getUri(), ServiceExtension.FEE_0_6.getUri()); + + /** Builder for {@link Fee}. */ + public static class Builder extends Buildable.Builder { + + /** Sets the cost of the fee. */ + public Builder setCost(BigDecimal cost) { + getInstance().cost = cost; + return this; + } + + /** Sets the description of the fee. */ + public Builder setDescription(String description) { + getInstance().description = description; + return this; + } + + /** Sets whether the fee is refundable. */ + public Builder setRefundable(Boolean refundable) { + getInstance().refundable = refundable; + return this; + } + + /** Sets the grace period of the fee. */ + public Builder setGracePeriod(Period gracePeriod) { + getInstance().gracePeriod = gracePeriod; + return this; + } + + /** Sets when the fee is applied. */ + public Builder setApplied(AppliedType applied) { + getInstance().applied = applied; + return this; + } + } } diff --git a/core/src/main/java/google/registry/model/domain/fee/FeeQueryCommandExtensionItem.java b/core/src/main/java/google/registry/model/domain/fee/FeeQueryCommandExtensionItem.java index 18bce08cc..d14f5e2f8 100644 --- a/core/src/main/java/google/registry/model/domain/fee/FeeQueryCommandExtensionItem.java +++ b/core/src/main/java/google/registry/model/domain/fee/FeeQueryCommandExtensionItem.java @@ -78,6 +78,10 @@ public abstract class FeeQueryCommandExtensionItem extends ImmutableObject { /** The period for the command being checked. */ Period period; + public void setPeriod(Period period) { + this.period = period; + } + /** * Three-character ISO4217 currency code. * diff --git a/core/src/main/java/google/registry/model/domain/fee/FeeTransformCommandExtension.java b/core/src/main/java/google/registry/model/domain/fee/FeeTransformCommandExtension.java index 6245fc062..df36b88cd 100644 --- a/core/src/main/java/google/registry/model/domain/fee/FeeTransformCommandExtension.java +++ b/core/src/main/java/google/registry/model/domain/fee/FeeTransformCommandExtension.java @@ -30,7 +30,7 @@ public abstract class FeeTransformCommandExtension extends ImmutableObject implements CommandExtension { /** The currency of the fee. */ - CurrencyUnit currency; + @XmlElement public CurrencyUnit currency; /** * The magnitude of the fee, in the specified units, with an optional description. @@ -38,7 +38,7 @@ public abstract class FeeTransformCommandExtension *

This is a list because a single operation can involve multiple fees. */ @XmlElement(name = "fee") - List fees; + public List fees; public CurrencyUnit getCurrency() { return currency; diff --git a/core/src/main/java/google/registry/model/domain/fee/FeeTransformResponseExtension.java b/core/src/main/java/google/registry/model/domain/fee/FeeTransformResponseExtension.java index b7afae101..9570790c8 100644 --- a/core/src/main/java/google/registry/model/domain/fee/FeeTransformResponseExtension.java +++ b/core/src/main/java/google/registry/model/domain/fee/FeeTransformResponseExtension.java @@ -31,7 +31,7 @@ import org.joda.money.CurrencyUnit; public class FeeTransformResponseExtension extends ImmutableObject implements ResponseExtension { /** The currency of the fee. */ - CurrencyUnit currency; + @XmlElement CurrencyUnit currency; /** * The magnitude of the fee, in the specified units, with an optional description. diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeCheckCommandExtensionItemV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeCheckCommandExtensionItemV06.java index 7719ce724..d06afae28 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeCheckCommandExtensionItemV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeCheckCommandExtensionItemV06.java @@ -14,6 +14,7 @@ package google.registry.model.domain.fee06; +import google.registry.model.domain.Period; import google.registry.model.domain.fee.FeeCheckCommandExtensionItem; import google.registry.model.domain.fee.FeeExtensionCommandDescriptor; import jakarta.xml.bind.annotation.XmlType; @@ -33,6 +34,16 @@ public class FeeCheckCommandExtensionItemV06 extends FeeCheckCommandExtensionIte /** The command being checked. */ FeeExtensionCommandDescriptor command; + public static FeeCheckCommandExtensionItemV06 create( + String name, CurrencyUnit currency, FeeExtensionCommandDescriptor command, Period period) { + FeeCheckCommandExtensionItemV06 instance = new FeeCheckCommandExtensionItemV06(); + instance.name = name; + instance.currency = currency; + instance.command = command; + instance.setPeriod(period); + return instance; + } + /** The name of the command being checked. */ @Override public CommandName getCommandName() { diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeCheckCommandExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeCheckCommandExtensionV06.java index 6d75de1d7..1a96fc24e 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeCheckCommandExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeCheckCommandExtensionV06.java @@ -25,16 +25,22 @@ import jakarta.xml.bind.annotation.XmlRootElement; import java.util.List; import org.joda.money.CurrencyUnit; -/** Version 0.6 of the fee extension that may be present on domain check commands. */ +/** + * An XML data object that represents version 0.6 of the fee extension that may be present on EPP + * domain check commands. + */ @XmlRootElement(name = "check") public class FeeCheckCommandExtensionV06 extends ImmutableObject implements FeeCheckCommandExtension< - FeeCheckCommandExtensionItemV06, - FeeCheckResponseExtensionV06> { + FeeCheckCommandExtensionItemV06, FeeCheckResponseExtensionV06> { @XmlElement(name = "domain") List items; + public void setItems(ImmutableList items) { + this.items = items; + } + @Override public CurrencyUnit getCurrency() { return null; // This version of the fee extension doesn't specify a top-level currency. diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeCreateCommandExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeCreateCommandExtensionV06.java index 7b4071496..80eb5ad47 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeCreateCommandExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeCreateCommandExtensionV06.java @@ -15,13 +15,20 @@ package google.registry.model.domain.fee06; import com.google.common.collect.ImmutableList; +import google.registry.model.Buildable; import google.registry.model.domain.fee.Credit; +import google.registry.model.domain.fee.Fee; import google.registry.model.domain.fee.FeeCreateCommandExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; import jakarta.xml.bind.annotation.XmlType; +import org.joda.money.CurrencyUnit; -/** A fee extension that may be present on domain create commands. */ +/** + * An XML data object that represents a fee extension that may be present on EPP domain create + * commands. + */ @XmlRootElement(name = "create") @XmlType(propOrder = {"currency", "fees"}) public class FeeCreateCommandExtensionV06 extends FeeCreateCommandExtension { @@ -31,12 +38,23 @@ public class FeeCreateCommandExtensionV06 extends FeeCreateCommandExtension { return new FeeTransformResponseExtension.Builder(new FeeCreateResponseExtensionV06()); } - /** - * This method is overridden and not annotated for JAXB because this version of the extension - * doesn't support the "credit" field. - */ + /** This version of the extension doesn't support the "credit" field. */ @Override + @XmlTransient public ImmutableList getCredits() { return ImmutableList.of(); } + + /** Builder for {@link FeeCreateCommandExtensionV06}. */ + public static class Builder extends Buildable.Builder { + public Builder setCurrency(CurrencyUnit currency) { + getInstance().currency = currency; + return this; + } + + public Builder setFees(ImmutableList fees) { + getInstance().fees = fees; + return this; + } + } } diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeCreateResponseExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeCreateResponseExtensionV06.java index b58dde263..410c2e2f2 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeCreateResponseExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeCreateResponseExtensionV06.java @@ -14,8 +14,6 @@ package google.registry.model.domain.fee06; -import com.google.common.collect.ImmutableList; -import google.registry.model.domain.fee.Credit; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; @@ -25,12 +23,5 @@ import jakarta.xml.bind.annotation.XmlType; * domain create commands. */ @XmlRootElement(name = "creData") -@XmlType(propOrder = {"currency", "fees"}) -public class FeeCreateResponseExtensionV06 extends FeeTransformResponseExtension { - - /** This version of the extension doesn't support the "credit" field. */ - @Override - public ImmutableList getCredits() { - return ImmutableList.of(); - } -} +@XmlType(propOrder = {"currency", "fees", "credits"}) +public class FeeCreateResponseExtensionV06 extends FeeTransformResponseExtension {} diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeDeleteResponseExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeDeleteResponseExtensionV06.java index 749865610..e7cbf4a10 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeDeleteResponseExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeDeleteResponseExtensionV06.java @@ -20,7 +20,7 @@ import jakarta.xml.bind.annotation.XmlType; /** * An XML data object that represents a fee extension that may be present on the response to EPP - * domain create commands. + * domain delete commands. */ @XmlRootElement(name = "delData") @XmlType(propOrder = {"currency", "fees", "credits"}) diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeRenewCommandExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeRenewCommandExtensionV06.java index 73e593913..8a43d77fa 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeRenewCommandExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeRenewCommandExtensionV06.java @@ -19,9 +19,13 @@ import google.registry.model.domain.fee.Credit; import google.registry.model.domain.fee.FeeRenewCommandExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; import jakarta.xml.bind.annotation.XmlType; -/** A fee extension that may be present on domain renew commands. */ +/** + * An XML data object that represents a fee extension that may be present on EPP domain renew + * commands. + */ @XmlRootElement(name = "renew") @XmlType(propOrder = {"currency", "fees"}) public class FeeRenewCommandExtensionV06 extends FeeRenewCommandExtension { @@ -33,6 +37,7 @@ public class FeeRenewCommandExtensionV06 extends FeeRenewCommandExtension { /** This version of the extension doesn't support the "credit" field. */ @Override + @XmlTransient public ImmutableList getCredits() { return ImmutableList.of(); } diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeRenewResponseExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeRenewResponseExtensionV06.java index 7f11aa5bc..07c58c3b9 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeRenewResponseExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeRenewResponseExtensionV06.java @@ -14,8 +14,6 @@ package google.registry.model.domain.fee06; -import com.google.common.collect.ImmutableList; -import google.registry.model.domain.fee.Credit; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; @@ -25,12 +23,5 @@ import jakarta.xml.bind.annotation.XmlType; * domain renew commands. */ @XmlRootElement(name = "renData") -@XmlType(propOrder = {"currency", "fees"}) -public class FeeRenewResponseExtensionV06 extends FeeTransformResponseExtension { - - /** This version of the extension doesn't support the "credit" field. */ - @Override - public ImmutableList getCredits() { - return super.getCredits(); - } -} +@XmlType(propOrder = {"currency", "fees", "credits"}) +public class FeeRenewResponseExtensionV06 extends FeeTransformResponseExtension {} diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeTransferCommandExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeTransferCommandExtensionV06.java index e3c1ec1f7..194586f25 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeTransferCommandExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeTransferCommandExtensionV06.java @@ -19,9 +19,13 @@ import google.registry.model.domain.fee.Credit; import google.registry.model.domain.fee.FeeTransferCommandExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; import jakarta.xml.bind.annotation.XmlType; -/** A fee extension that may be present on domain transfer requests. */ +/** + * An XML data object that represents a fee extension that may be present on EPP domain transfer + * commands. + */ @XmlRootElement(name = "transfer") @XmlType(propOrder = {"currency", "fees"}) public class FeeTransferCommandExtensionV06 extends FeeTransferCommandExtension { @@ -33,6 +37,7 @@ public class FeeTransferCommandExtensionV06 extends FeeTransferCommandExtension /** This version of the extension doesn't support the "credit" field. */ @Override + @XmlTransient public ImmutableList getCredits() { return ImmutableList.of(); } diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeTransferResponseExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeTransferResponseExtensionV06.java index 1d92391b2..b632d43ac 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeTransferResponseExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeTransferResponseExtensionV06.java @@ -14,23 +14,14 @@ package google.registry.model.domain.fee06; -import com.google.common.collect.ImmutableList; -import google.registry.model.domain.fee.Credit; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; /** * An XML data object that represents a fee extension that may be present on the response to EPP - * domain transfer requests. + * domain transfer commands. */ @XmlRootElement(name = "trnData") -@XmlType(propOrder = {"currency", "fees"}) -public class FeeTransferResponseExtensionV06 extends FeeTransformResponseExtension { - - /** This version of the extension doesn't support the "credit" field. */ - @Override - public ImmutableList getCredits() { - return super.getCredits(); - } -} +@XmlType(propOrder = {"currency", "fees", "credits"}) +public class FeeTransferResponseExtensionV06 extends FeeTransformResponseExtension {} diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeUpdateCommandExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeUpdateCommandExtensionV06.java index 3fce12cba..d6327098d 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeUpdateCommandExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeUpdateCommandExtensionV06.java @@ -19,9 +19,13 @@ import google.registry.model.domain.fee.Credit; import google.registry.model.domain.fee.FeeTransformResponseExtension; import google.registry.model.domain.fee.FeeUpdateCommandExtension; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; import jakarta.xml.bind.annotation.XmlType; -/** A fee extension that may be present on domain update commands. */ +/** + * An XML data object that represents a fee extension that may be present on EPP domain update + * commands. + */ @XmlRootElement(name = "update") @XmlType(propOrder = {"currency", "fees"}) public class FeeUpdateCommandExtensionV06 extends FeeUpdateCommandExtension { @@ -33,6 +37,7 @@ public class FeeUpdateCommandExtensionV06 extends FeeUpdateCommandExtension { /** This version of the extension doesn't support the "credit" field. */ @Override + @XmlTransient public ImmutableList getCredits() { return ImmutableList.of(); } diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeUpdateResponseExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeUpdateResponseExtensionV06.java index 154b78d76..9b6245929 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeUpdateResponseExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeUpdateResponseExtensionV06.java @@ -14,8 +14,6 @@ package google.registry.model.domain.fee06; -import com.google.common.collect.ImmutableList; -import google.registry.model.domain.fee.Credit; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; @@ -25,12 +23,5 @@ import jakarta.xml.bind.annotation.XmlType; * domain update commands. */ @XmlRootElement(name = "updData") -@XmlType(propOrder = {"currency", "fees"}) -public class FeeUpdateResponseExtensionV06 extends FeeTransformResponseExtension { - - /** This version of the extension doesn't support the "credit" field. */ - @Override - public ImmutableList getCredits() { - return super.getCredits(); - } -} +@XmlType(propOrder = {"currency", "fees", "credits"}) +public class FeeUpdateResponseExtensionV06 extends FeeTransformResponseExtension {} diff --git a/core/src/main/java/google/registry/model/domain/fee12/FeeCreateCommandExtensionV12.java b/core/src/main/java/google/registry/model/domain/fee12/FeeCreateCommandExtensionV12.java index 2ff8fcbe1..9aed6ef09 100644 --- a/core/src/main/java/google/registry/model/domain/fee12/FeeCreateCommandExtensionV12.java +++ b/core/src/main/java/google/registry/model/domain/fee12/FeeCreateCommandExtensionV12.java @@ -17,13 +17,16 @@ package google.registry.model.domain.fee12; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableList; +import google.registry.model.Buildable; import google.registry.model.domain.fee.Credit; +import google.registry.model.domain.fee.Fee; import google.registry.model.domain.fee.FeeCreateCommandExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; import java.util.List; +import org.joda.money.CurrencyUnit; /** A fee extension that may be present on domain create commands. */ @XmlRootElement(name = "create") @@ -42,4 +45,22 @@ public class FeeCreateCommandExtensionV12 extends FeeCreateCommandExtension { public FeeTransformResponseExtension.Builder createResponseBuilder() { return new FeeTransformResponseExtension.Builder(new FeeCreateResponseExtensionV12()); } + + /** Builder for {@link FeeCreateCommandExtensionV12}. */ + public static class Builder extends Buildable.Builder { + public Builder setCurrency(CurrencyUnit currency) { + getInstance().currency = currency; + return this; + } + + public Builder setFees(ImmutableList fees) { + getInstance().fees = fees; + return this; + } + + public Builder setCredits(ImmutableList credits) { + getInstance().credits = credits; + return this; + } + } } diff --git a/core/src/main/java/google/registry/model/domain/fee12/FeeRenewCommandExtensionV12.java b/core/src/main/java/google/registry/model/domain/fee12/FeeRenewCommandExtensionV12.java index d1a0b120f..f9f9e886c 100644 --- a/core/src/main/java/google/registry/model/domain/fee12/FeeRenewCommandExtensionV12.java +++ b/core/src/main/java/google/registry/model/domain/fee12/FeeRenewCommandExtensionV12.java @@ -17,18 +17,21 @@ package google.registry.model.domain.fee12; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableList; +import google.registry.model.Buildable; import google.registry.model.domain.fee.Credit; +import google.registry.model.domain.fee.Fee; import google.registry.model.domain.fee.FeeRenewCommandExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; import java.util.List; +import org.joda.money.CurrencyUnit; /** A fee extension that may be present on domain renew commands. */ @XmlRootElement(name = "renew") @XmlType(propOrder = {"currency", "fees", "credits"}) -public class FeeRenewCommandExtensionV12 extends FeeRenewCommandExtension { +public class FeeRenewCommandExtensionV12 extends FeeRenewCommandExtension { @XmlElement(name = "credit") List credits; @@ -42,4 +45,22 @@ public class FeeRenewCommandExtensionV12 extends FeeRenewCommandExtension { public FeeTransformResponseExtension.Builder createResponseBuilder() { return new FeeTransformResponseExtension.Builder(new FeeRenewResponseExtensionV12()); } + + /** Builder for {@link FeeRenewCommandExtensionV12}. */ + public static class Builder extends Buildable.Builder { + public Builder setCurrency(CurrencyUnit currency) { + getInstance().currency = currency; + return this; + } + + public Builder setFees(ImmutableList fees) { + getInstance().fees = fees; + return this; + } + + public Builder setCredits(ImmutableList credits) { + getInstance().credits = credits; + return this; + } + } } diff --git a/core/src/main/java/google/registry/model/domain/fee12/FeeTransferCommandExtensionV12.java b/core/src/main/java/google/registry/model/domain/fee12/FeeTransferCommandExtensionV12.java index 53049bb9d..f9800a1c3 100644 --- a/core/src/main/java/google/registry/model/domain/fee12/FeeTransferCommandExtensionV12.java +++ b/core/src/main/java/google/registry/model/domain/fee12/FeeTransferCommandExtensionV12.java @@ -17,13 +17,16 @@ package google.registry.model.domain.fee12; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableList; +import google.registry.model.Buildable; import google.registry.model.domain.fee.Credit; +import google.registry.model.domain.fee.Fee; import google.registry.model.domain.fee.FeeTransferCommandExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; import java.util.List; +import org.joda.money.CurrencyUnit; /** A fee extension that may be present on domain transfer requests. */ @XmlRootElement(name = "transfer") @@ -42,4 +45,22 @@ public class FeeTransferCommandExtensionV12 extends FeeTransferCommandExtension public FeeTransformResponseExtension.Builder createResponseBuilder() { return new FeeTransformResponseExtension.Builder(new FeeTransferResponseExtensionV12()); } + + /** Builder for {@link FeeTransferCommandExtensionV12}. */ + public static class Builder extends Buildable.Builder { + public Builder setCurrency(CurrencyUnit currency) { + getInstance().currency = currency; + return this; + } + + public Builder setFees(ImmutableList fees) { + getInstance().fees = fees; + return this; + } + + public Builder setCredits(ImmutableList credits) { + getInstance().credits = credits; + return this; + } + } } diff --git a/core/src/main/java/google/registry/model/domain/fee12/FeeUpdateCommandExtensionV12.java b/core/src/main/java/google/registry/model/domain/fee12/FeeUpdateCommandExtensionV12.java index 71a430093..86a600487 100644 --- a/core/src/main/java/google/registry/model/domain/fee12/FeeUpdateCommandExtensionV12.java +++ b/core/src/main/java/google/registry/model/domain/fee12/FeeUpdateCommandExtensionV12.java @@ -17,13 +17,16 @@ package google.registry.model.domain.fee12; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableList; +import google.registry.model.Buildable; import google.registry.model.domain.fee.Credit; +import google.registry.model.domain.fee.Fee; import google.registry.model.domain.fee.FeeTransformResponseExtension; import google.registry.model.domain.fee.FeeUpdateCommandExtension; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; import java.util.List; +import org.joda.money.CurrencyUnit; /** A fee extension that may be present on domain update commands. */ @XmlRootElement(name = "update") @@ -42,4 +45,22 @@ public class FeeUpdateCommandExtensionV12 extends FeeUpdateCommandExtension { public FeeTransformResponseExtension.Builder createResponseBuilder() { return new FeeTransformResponseExtension.Builder(new FeeUpdateResponseExtensionV12()); } + + /** Builder for {@link FeeUpdateCommandExtensionV12}. */ + public static class Builder extends Buildable.Builder { + public Builder setCurrency(CurrencyUnit currency) { + getInstance().currency = currency; + return this; + } + + public Builder setFees(ImmutableList fees) { + getInstance().fees = fees; + return this; + } + + public Builder setCredits(ImmutableList credits) { + getInstance().credits = credits; + return this; + } + } } diff --git a/core/src/main/java/google/registry/model/domain/launch/LaunchCheckExtension.java b/core/src/main/java/google/registry/model/domain/launch/LaunchCheckExtension.java index 5c3d2d0b8..b3072f7fb 100644 --- a/core/src/main/java/google/registry/model/domain/launch/LaunchCheckExtension.java +++ b/core/src/main/java/google/registry/model/domain/launch/LaunchCheckExtension.java @@ -19,34 +19,37 @@ import static com.google.common.base.MoreObjects.firstNonNull; import google.registry.model.ImmutableObject; import google.registry.model.eppinput.EppInput.CommandExtension; import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlEnumValue; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlType; /** * An XML data object that represents a launch extension that may be present on EPP domain check * commands. * *

This object holds XML data which JAXB will unmarshal from an EPP domain check command - * extension. The XML will have the following enclosing structure: + * extension. The XML will have the following enclosing structure: * - *

 {@code
- *   
- *     
- *       
- *         
- *       
- *       
- *         
- *           
- *         
- *       
- *     
- *   
- * } 
+ *
{@code
+ * 
+ *   
+ *     
+ *       
+ *     
+ *     
+ *       
+ *         
+ *       
+ *     
+ *   
+ * 
+ * }
* * @see CommandExtension */ @XmlRootElement(name = "check") +@XmlType(propOrder = "phase") public class LaunchCheckExtension extends ImmutableObject implements CommandExtension { /** The default check type is "claims" if not specified. */ @@ -67,11 +70,18 @@ public class LaunchCheckExtension extends ImmutableObject implements CommandExte * The launch phase this command is intended to run against. If it does not match the server's * current launch phase, the command will be rejected. */ - LaunchPhase phase; + @XmlElement LaunchPhase phase; @XmlAttribute CheckType type; + public static LaunchCheckExtension create(CheckType type, LaunchPhase phase) { + LaunchCheckExtension instance = new LaunchCheckExtension(); + instance.type = type; + instance.phase = phase; + return instance; + } + public CheckType getCheckType() { return firstNonNull(type, DEFAULT_CHECK_TYPE); } diff --git a/core/src/main/java/google/registry/model/domain/metadata/MetadataExtension.java b/core/src/main/java/google/registry/model/domain/metadata/MetadataExtension.java index f098982c7..5d7f41fff 100644 --- a/core/src/main/java/google/registry/model/domain/metadata/MetadataExtension.java +++ b/core/src/main/java/google/registry/model/domain/metadata/MetadataExtension.java @@ -14,39 +14,62 @@ package google.registry.model.domain.metadata; +import static com.google.common.base.MoreObjects.firstNonNull; + +import google.registry.model.Buildable; import google.registry.model.ImmutableObject; import google.registry.model.eppinput.EppInput.CommandExtension; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlType; +import javax.annotation.Nullable; -/** A metadata extension that may be present on EPP create/mutate commands. */ +/** + * Extension for EPP commands that provides metadata. + * + * @see EPP Metadata Extension + */ @XmlRootElement(name = "metadata") +@XmlType(propOrder = {"reason", "requestedByRegistrar", "isAnchorTenant"}) public class MetadataExtension extends ImmutableObject implements CommandExtension { - /** The reason for the change. */ - @XmlElement(name = "reason") - String reason; + /** Reason for the command. */ + @XmlElement @Nullable String reason; - /** Whether a change was requested by a registrar. */ - @XmlElement(name = "requestedByRegistrar") - boolean requestedByRegistrar; + /** Whether the command was requested by a registrar. */ + @XmlElement Boolean requestedByRegistrar; - /** - * Whether a domain is being created for an anchor tenant. This field is only - * relevant for domain creates, and should be omitted for all other operations. - */ + /** Whether this is an anchor tenant. */ @XmlElement(name = "anchorTenant") - boolean isAnchorTenant; + Boolean isAnchorTenant; public String getReason() { return reason; } - public boolean getRequestedByRegistrar() { + public Boolean getRequestedByRegistrar() { return requestedByRegistrar; } - public boolean getIsAnchorTenant() { - return isAnchorTenant; + public Boolean getIsAnchorTenant() { + return firstNonNull(isAnchorTenant, false); + } + + /** Builder for {@link MetadataExtension}. */ + public static class Builder extends Buildable.Builder { + public Builder setReason(String reason) { + getInstance().reason = reason; + return this; + } + + public Builder setRequestedByRegistrar(Boolean requestedByRegistrar) { + getInstance().requestedByRegistrar = requestedByRegistrar; + return this; + } + + public Builder setAnchorTenant(Boolean isAnchorTenant) { + getInstance().isAnchorTenant = isAnchorTenant; + return this; + } } } diff --git a/core/src/main/java/google/registry/model/domain/secdns/DomainDsDataBase.java b/core/src/main/java/google/registry/model/domain/secdns/DomainDsDataBase.java index 82195b819..81509fac3 100644 --- a/core/src/main/java/google/registry/model/domain/secdns/DomainDsDataBase.java +++ b/core/src/main/java/google/registry/model/domain/secdns/DomainDsDataBase.java @@ -23,12 +23,14 @@ import jakarta.persistence.Transient; import jakarta.xml.bind.DatatypeConverter; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlTransient; +import jakarta.xml.bind.annotation.XmlType; import jakarta.xml.bind.annotation.adapters.HexBinaryAdapter; import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; /** Base class for {@link DomainDsData} and {@link DomainDsDataHistory}. */ @MappedSuperclass @Access(AccessType.FIELD) +@XmlType(propOrder = {"keyTag", "algorithm", "digestType", "digest"}) public abstract class DomainDsDataBase extends ImmutableObject implements UnsafeSerializable { @XmlTransient @Transient @Insignificant String domainRepoId; diff --git a/core/src/main/java/google/registry/model/domain/secdns/SecDnsCreateExtension.java b/core/src/main/java/google/registry/model/domain/secdns/SecDnsCreateExtension.java index a7a12e796..b3297915e 100644 --- a/core/src/main/java/google/registry/model/domain/secdns/SecDnsCreateExtension.java +++ b/core/src/main/java/google/registry/model/domain/secdns/SecDnsCreateExtension.java @@ -14,11 +14,13 @@ package google.registry.model.domain.secdns; -import static google.registry.util.CollectionUtils.nullSafeImmutableCopy; +import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableSet; +import google.registry.model.Buildable; import google.registry.model.ImmutableObject; import google.registry.model.eppinput.EppInput.CommandExtension; +import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; import java.util.Set; @@ -33,9 +35,10 @@ public class SecDnsCreateExtension extends ImmutableObject implements CommandExt *

We do not support expirations, but we need this field to be able to return appropriate * errors. */ - Long maxSigLife; + @XmlElement Long maxSigLife; /** Signatures for this domain. */ + @XmlElement(name = "dsData") Set dsData; public Long getMaxSigLife() { @@ -43,6 +46,19 @@ public class SecDnsCreateExtension extends ImmutableObject implements CommandExt } public ImmutableSet getDsData() { - return nullSafeImmutableCopy(dsData); + return nullToEmptyImmutableCopy(dsData); + } + + /** Builder for {@link SecDnsCreateExtension}. */ + public static class Builder extends Buildable.Builder { + public Builder setDsData(ImmutableSet dsData) { + getInstance().dsData = dsData; + return this; + } + + public Builder setMaxSigLife(Long maxSigLife) { + getInstance().maxSigLife = maxSigLife; + return this; + } } } diff --git a/core/src/main/java/google/registry/model/domain/secdns/SecDnsUpdateExtension.java b/core/src/main/java/google/registry/model/domain/secdns/SecDnsUpdateExtension.java index a9032c277..9001a0863 100644 --- a/core/src/main/java/google/registry/model/domain/secdns/SecDnsUpdateExtension.java +++ b/core/src/main/java/google/registry/model/domain/secdns/SecDnsUpdateExtension.java @@ -17,6 +17,7 @@ package google.registry.model.domain.secdns; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableSet; +import google.registry.model.Buildable; import google.registry.model.ImmutableObject; import google.registry.model.eppinput.EppInput.CommandExtension; import jakarta.xml.bind.annotation.XmlAttribute; @@ -46,7 +47,7 @@ public class SecDnsUpdateExtension extends ImmutableObject implements CommandExt Remove remove; /** Allows adding new delegations. */ - Add add; + @XmlElement Add add; /** Would allow changing maxSigLife except that we don't support it. */ @XmlElement(name = "chg") @@ -68,31 +69,88 @@ public class SecDnsUpdateExtension extends ImmutableObject implements CommandExt return Optional.ofNullable(change); } + /** Builder for {@link SecDnsUpdateExtension}. */ + public static class Builder extends Buildable.Builder { + public Builder setUrgent(Boolean urgent) { + getInstance().urgent = urgent; + return this; + } + + public Builder setRemove(Remove remove) { + getInstance().remove = remove; + return this; + } + + public Builder setAdd(Add add) { + getInstance().add = add; + return this; + } + } + @XmlTransient abstract static class AddRemoveBase extends ImmutableObject { - /** Delegations to add or remove. */ + abstract static class Builder> + extends Buildable.Builder { + public abstract B setDsData(ImmutableSet dsData); + } + } + + /** The inner add type on the update extension. */ + @XmlType(propOrder = "dsData") + public static class Add extends AddRemoveBase { + /** Delegations to add. */ + @XmlElement(name = "dsData") Set dsData; public ImmutableSet getDsData() { return nullToEmptyImmutableCopy(dsData); } - } - /** The inner add type on the update extension. */ - public static class Add extends AddRemoveBase {} + /** Builder for {@link Add}. */ + public static class Builder extends AddRemoveBase.Builder { + @Override + public Builder setDsData(ImmutableSet dsData) { + getInstance().dsData = dsData; + return this; + } + } + } /** The inner remove type on the update extension. */ @XmlType(propOrder = {"all", "dsData"}) public static class Remove extends AddRemoveBase { /** Whether to remove all delegations. */ - Boolean all; + @XmlElement Boolean all; + + /** Delegations to remove. */ + @XmlElement(name = "dsData") + Set dsData; public Boolean getAll() { return all; } + + public ImmutableSet getDsData() { + return nullToEmptyImmutableCopy(dsData); + } + + /** Builder for {@link Remove}. */ + public static class Builder extends AddRemoveBase.Builder { + public Builder setAll(Boolean all) { + getInstance().all = all; + return this; + } + + @Override + public Builder setDsData(ImmutableSet dsData) { + getInstance().dsData = dsData; + return this; + } + } } /** The inner change type on the update extension, though we don't actually support changes. */ + @XmlType(propOrder = "maxSigLife") public static class Change extends ImmutableObject { /** * Time in seconds until the signature should expire. @@ -100,6 +158,7 @@ public class SecDnsUpdateExtension extends ImmutableObject implements CommandExt *

We do not support expirations, but we need this field to be able to return appropriate * errors. */ + @XmlElement(name = "maxSigLife") Long maxSigLife; } } diff --git a/core/src/main/java/google/registry/model/domain/superuser/DomainDeleteSuperuserExtension.java b/core/src/main/java/google/registry/model/domain/superuser/DomainDeleteSuperuserExtension.java index e63d895b0..2b7151f68 100644 --- a/core/src/main/java/google/registry/model/domain/superuser/DomainDeleteSuperuserExtension.java +++ b/core/src/main/java/google/registry/model/domain/superuser/DomainDeleteSuperuserExtension.java @@ -16,9 +16,11 @@ package google.registry.model.domain.superuser; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlType; /** A superuser extension that may be present on domain delete commands. */ @XmlRootElement(name = "domainDelete") +@XmlType(propOrder = {"redemptionGracePeriodDays", "pendingDeleteDays"}) public class DomainDeleteSuperuserExtension extends SuperuserExtension { @XmlElement(name = "redemptionGracePeriodDays") @@ -27,6 +29,14 @@ public class DomainDeleteSuperuserExtension extends SuperuserExtension { @XmlElement(name = "pendingDeleteDays") int pendingDeleteDays; + public static DomainDeleteSuperuserExtension create( + int redemptionGracePeriodDays, int pendingDeleteDays) { + DomainDeleteSuperuserExtension instance = new DomainDeleteSuperuserExtension(); + instance.redemptionGracePeriodDays = redemptionGracePeriodDays; + instance.pendingDeleteDays = pendingDeleteDays; + return instance; + } + public int getRedemptionGracePeriodDays() { return redemptionGracePeriodDays; } diff --git a/core/src/main/java/google/registry/model/domain/superuser/DomainUpdateSuperuserExtension.java b/core/src/main/java/google/registry/model/domain/superuser/DomainUpdateSuperuserExtension.java index ac715d90c..54552bd3a 100644 --- a/core/src/main/java/google/registry/model/domain/superuser/DomainUpdateSuperuserExtension.java +++ b/core/src/main/java/google/registry/model/domain/superuser/DomainUpdateSuperuserExtension.java @@ -14,22 +14,28 @@ package google.registry.model.domain.superuser; -import static com.google.common.base.Strings.isNullOrEmpty; - import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlType; import java.util.Optional; import javax.annotation.Nullable; /** A superuser extension that may be present on domain update commands. */ @XmlRootElement(name = "domainUpdate") +@XmlType(propOrder = "autorenews") public class DomainUpdateSuperuserExtension extends SuperuserExtension { @XmlElement(name = "autorenews") @Nullable - String autorenews; + Boolean autorenews; + + public static DomainUpdateSuperuserExtension create(@Nullable Boolean autorenews) { + DomainUpdateSuperuserExtension instance = new DomainUpdateSuperuserExtension(); + instance.autorenews = autorenews; + return instance; + } public Optional getAutorenews() { - return Optional.ofNullable(isNullOrEmpty(autorenews) ? null : Boolean.valueOf(autorenews)); + return Optional.ofNullable(autorenews); } } diff --git a/core/src/main/java/google/registry/model/domain/token/AllocationTokenExtension.java b/core/src/main/java/google/registry/model/domain/token/AllocationTokenExtension.java index fdeee6285..327d1d7ea 100644 --- a/core/src/main/java/google/registry/model/domain/token/AllocationTokenExtension.java +++ b/core/src/main/java/google/registry/model/domain/token/AllocationTokenExtension.java @@ -35,6 +35,12 @@ public class AllocationTokenExtension extends ImmutableObject implements Command @XmlJavaTypeAdapter(TrimWhitespaceAdapter.class) String allocationToken; + public static AllocationTokenExtension create(String allocationToken) { + AllocationTokenExtension instance = new AllocationTokenExtension(); + instance.allocationToken = allocationToken; + return instance; + } + public String getAllocationToken() { return allocationToken; } diff --git a/core/src/main/java/google/registry/model/eppinput/EppExtensions.java b/core/src/main/java/google/registry/model/eppinput/EppExtensions.java new file mode 100644 index 000000000..43891fbfe --- /dev/null +++ b/core/src/main/java/google/registry/model/eppinput/EppExtensions.java @@ -0,0 +1,181 @@ +// Copyright 2026 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.eppinput; + +import static com.google.common.base.Strings.isNullOrEmpty; +import static google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName.CREATE; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import google.registry.model.domain.Period; +import google.registry.model.domain.fee.Fee; +import google.registry.model.domain.fee.FeeExtensionCommandDescriptor; +import google.registry.model.domain.fee06.FeeCheckCommandExtensionItemV06; +import google.registry.model.domain.fee06.FeeCheckCommandExtensionV06; +import google.registry.model.domain.fee06.FeeCreateCommandExtensionV06; +import google.registry.model.domain.fee12.FeeCreateCommandExtensionV12; +import google.registry.model.domain.launch.LaunchCheckExtension; +import google.registry.model.domain.launch.LaunchCheckExtension.CheckType; +import google.registry.model.domain.launch.LaunchPhase; +import google.registry.model.domain.metadata.MetadataExtension; +import google.registry.model.domain.secdns.DomainDsData; +import google.registry.model.domain.secdns.SecDnsCreateExtension; +import google.registry.model.domain.secdns.SecDnsUpdateExtension; +import google.registry.model.domain.secdns.SecDnsUpdateExtension.Add; +import google.registry.model.domain.secdns.SecDnsUpdateExtension.Remove; +import google.registry.model.domain.superuser.DomainDeleteSuperuserExtension; +import google.registry.model.domain.superuser.DomainUpdateSuperuserExtension; +import google.registry.model.domain.token.AllocationTokenExtension; +import java.math.BigDecimal; +import javax.annotation.Nullable; +import org.joda.money.CurrencyUnit; +import org.joda.money.Money; + +/** Static helpers for creating common EPP extensions. */ +public class EppExtensions { + + /** + * Returns a metadata extension with the specified reason and flags. + * + * @param reason the reason for the change, recorded in history entries + * @param requestedByRegistrar whether the change was requested by a registrar + * @param isAnchorTenant whether the domain is an anchor tenant + */ + @Nullable + public static MetadataExtension metadata( + @Nullable String reason, + @Nullable Boolean requestedByRegistrar, + @Nullable Boolean isAnchorTenant) { + if (isNullOrEmpty(reason) && requestedByRegistrar == null && isAnchorTenant == null) { + return null; + } + return new MetadataExtension.Builder() + .setReason(reason) + .setRequestedByRegistrar(requestedByRegistrar) + .setAnchorTenant(isAnchorTenant) + .build(); + } + + /** Returns a metadata extension for standard tool commands. */ + @Nullable + public static MetadataExtension toolMetadata( + @Nullable String reason, @Nullable Boolean requestedByRegistrar) { + return metadata(reason, requestedByRegistrar, null); + } + + /** Returns an allocation token extension for the specified token string. */ + @Nullable + public static AllocationTokenExtension allocationToken(@Nullable String token) { + return isNullOrEmpty(token) ? null : AllocationTokenExtension.create(token); + } + + /** Returns a domain update superuser extension with the specified autorenew flag. */ + @Nullable + public static DomainUpdateSuperuserExtension updateSuperuser(@Nullable Boolean autorenews) { + return autorenews == null ? null : DomainUpdateSuperuserExtension.create(autorenews); + } + + /** Returns a domain delete superuser extension for immediate deletion if requested. */ + @Nullable + public static DomainDeleteSuperuserExtension deleteSuperuser(boolean immediately) { + return immediately ? DomainDeleteSuperuserExtension.create(0, 0) : null; + } + + /** Returns a fee create extension (V12) for a single fee. */ + @Nullable + public static FeeCreateCommandExtensionV12 feeCreate(@Nullable Money cost) { + return cost == null ? null : feeCreate(cost.getCurrencyUnit(), cost.getAmount()); + } + + /** Returns a fee create extension (V12) for a single fee with a simple currency and cost. */ + @Nullable + public static FeeCreateCommandExtensionV12 feeCreate( + @Nullable CurrencyUnit currency, @Nullable BigDecimal cost) { + if (currency == null || cost == null) { + return null; + } + return new FeeCreateCommandExtensionV12.Builder() + .setCurrency(currency) + .setFees(ImmutableList.of(new Fee.Builder().setCost(cost).build())) + .build(); + } + + /** Returns a fee create extension (V06) for a single fee. */ + @Nullable + public static FeeCreateCommandExtensionV06 feeCreateV06(@Nullable Money cost) { + if (cost == null) { + return null; + } + return new FeeCreateCommandExtensionV06.Builder() + .setCurrency(cost.getCurrencyUnit()) + .setFees(ImmutableList.of(new Fee.Builder().setCost(cost.getAmount()).build())) + .build(); + } + + /** Returns a secDNS create extension with the specified DS records. */ + @Nullable + public static SecDnsCreateExtension secDnsCreate(ImmutableSet dsData) { + if (dsData.isEmpty()) { + return null; + } + return new SecDnsCreateExtension.Builder().setDsData(dsData).build(); + } + + /** Returns a secDNS update extension to replace or modify DS records. */ + @Nullable + public static SecDnsUpdateExtension secDnsUpdate( + ImmutableSet add, ImmutableSet remove, boolean removeAll) { + if (add.isEmpty() && remove.isEmpty() && !removeAll) { + return null; + } + SecDnsUpdateExtension.Builder builder = new SecDnsUpdateExtension.Builder(); + if (removeAll) { + builder.setRemove(new Remove.Builder().setAll(true).build()); + } else if (!remove.isEmpty()) { + builder.setRemove(new Remove.Builder().setDsData(remove).build()); + } + if (!add.isEmpty()) { + builder.setAdd(new Add.Builder().setDsData(add).build()); + } + return builder.build(); + } + + /** Returns a fee check extension for domain creations (V06). */ + public static FeeCheckCommandExtensionV06 feeCheckCreateV06(ImmutableList domainNames) { + return feeCheckCreateV06(domainNames, 1); + } + + /** Returns a fee check extension for domain creations (V06) with a specific period. */ + public static FeeCheckCommandExtensionV06 feeCheckCreateV06( + ImmutableList domainNames, int years) { + FeeCheckCommandExtensionV06 feeCheck = new FeeCheckCommandExtensionV06(); + ImmutableList.Builder items = new ImmutableList.Builder<>(); + for (String domainName : domainNames) { + items.add( + FeeCheckCommandExtensionItemV06.create( + domainName, + null, + FeeExtensionCommandDescriptor.create(CREATE, null, null), + Period.create(years, Period.Unit.YEARS))); + } + feeCheck.setItems(items.build()); + return feeCheck; + } + + /** Returns a launch check extension for claims. */ + public static LaunchCheckExtension launchCheckClaims() { + return LaunchCheckExtension.create(CheckType.CLAIMS, LaunchPhase.CLAIMS); + } +} diff --git a/core/src/main/java/google/registry/model/eppinput/EppInput.java b/core/src/main/java/google/registry/model/eppinput/EppInput.java index 90425f95c..a16b6b74c 100644 --- a/core/src/main/java/google/registry/model/eppinput/EppInput.java +++ b/core/src/main/java/google/registry/model/eppinput/EppInput.java @@ -14,12 +14,14 @@ package google.registry.model.eppinput; +import static google.registry.util.CollectionUtils.isNullOrEmpty; import static google.registry.util.CollectionUtils.nullSafeImmutableCopy; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.base.Ascii; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import google.registry.model.Buildable; import google.registry.model.ImmutableObject; import google.registry.model.domain.DomainCommand; import google.registry.model.domain.bulktoken.BulkTokenExtension; @@ -60,6 +62,8 @@ import google.registry.model.domain.token.AllocationTokenExtension; import google.registry.model.eppinput.ResourceCommand.ResourceCheck; import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand; import google.registry.model.host.HostCommand; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAttribute; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlElementRef; @@ -69,21 +73,26 @@ import jakarta.xml.bind.annotation.XmlElements; import jakarta.xml.bind.annotation.XmlEnumValue; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlSchema; +import jakarta.xml.bind.annotation.XmlTransient; import jakarta.xml.bind.annotation.XmlType; import jakarta.xml.bind.annotation.adapters.XmlAdapter; import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.Set; import javax.annotation.Nullable; /** This class represents the root EPP XML element for input. */ @XmlRootElement(name = "epp") +@XmlAccessorType(XmlAccessType.FIELD) public class EppInput extends ImmutableObject { @XmlElements({ - @XmlElement(name = "command", type = CommandWrapper.class), - @XmlElement(name = "hello", type = Hello.class) }) + @XmlElement(name = "command", type = CommandWrapper.class), + @XmlElement(name = "hello", type = Hello.class) + }) CommandWrapper commandWrapper; public CommandWrapper getCommandWrapper() { @@ -107,11 +116,11 @@ public class EppInput extends ImmutableObject { public Optional getResourceType() { ResourceCommand resourceCommand = getResourceCommand(); if (resourceCommand != null) { - XmlSchema xmlSchemaAnnotation = - resourceCommand.getClass().getPackage().getAnnotation(XmlSchema.class); - if (xmlSchemaAnnotation != null && xmlSchemaAnnotation.xmlns().length > 0) { - return Optional.of(xmlSchemaAnnotation.xmlns()[0].prefix()); - } + XmlSchema xmlSchemaAnnotation = + resourceCommand.getClass().getPackage().getAnnotation(XmlSchema.class); + if (xmlSchemaAnnotation != null && xmlSchemaAnnotation.xmlns().length > 0) { + return Optional.of(xmlSchemaAnnotation.xmlns()[0].prefix()); + } } return Optional.empty(); } @@ -123,6 +132,9 @@ public class EppInput extends ImmutableObject { @Nullable private ResourceCommand getResourceCommand() { + if (commandWrapper == null) { + return null; + } InnerCommand innerCommand = commandWrapper.getCommand(); return innerCommand instanceof ResourceCommandWrapper resourceCommandWrapper ? resourceCommandWrapper.getResourceCommand() @@ -136,7 +148,7 @@ public class EppInput extends ImmutableObject { public Optional getSingleTargetId() { ResourceCommand resourceCommand = getResourceCommand(); return resourceCommand instanceof SingleResourceCommand singleResourceCommand - ? Optional.of(singleResourceCommand.getTargetId()) + ? Optional.ofNullable(singleResourceCommand.getTargetId()) : Optional.empty(); } @@ -147,7 +159,8 @@ public class EppInput extends ImmutableObject { public ImmutableList getTargetIds() { ResourceCommand resourceCommand = getResourceCommand(); if (resourceCommand instanceof SingleResourceCommand singleResourceCommand) { - return ImmutableList.of(singleResourceCommand.getTargetId()); + String targetId = singleResourceCommand.getTargetId(); + return targetId == null ? ImmutableList.of() : ImmutableList.of(targetId); } else if (resourceCommand instanceof ResourceCheck resourceCheck) { return resourceCheck.getTargetIds(); } else { @@ -157,17 +170,53 @@ public class EppInput extends ImmutableObject { /** Get the extension based on type, or null. If there are multiple, it chooses the first. */ public Optional getSingleExtension(Class clazz) { - return getCommandWrapper().getExtensions().stream() + if (commandWrapper == null) { + return Optional.empty(); + } + return commandWrapper.getExtensions().stream() .filter(clazz::isInstance) .map(clazz::cast) .findFirst(); } + /** + * Static factory method to create an {@link EppInput} from an {@link InnerCommand} and + * extensions. + */ + public static EppInput create(InnerCommand command, CommandExtension... extensions) { + EppInput instance = new EppInput(); + instance.commandWrapper = new CommandWrapper(); + instance.commandWrapper.command = command; + ImmutableList validExtensions = + Arrays.stream(extensions).filter(Objects::nonNull).collect(ImmutableList.toImmutableList()); + if (!validExtensions.isEmpty()) { + instance.commandWrapper.extension = validExtensions; + } + return instance; + } + + public EppInput withClTrid(String clTrid) { + this.commandWrapper.clTrid = clTrid; + return this; + } + + /** Builder for {@link EppInput}. */ + public static class Builder extends Buildable.Builder { + public Builder setCommandWrapper(CommandWrapper commandWrapper) { + getInstance().commandWrapper = commandWrapper; + return this; + } + } + /** A tag that goes inside an EPP {@literal }. */ - public static class InnerCommand extends ImmutableObject {} + @XmlTransient + @XmlAccessorType(XmlAccessType.FIELD) + public abstract static class InnerCommand extends ImmutableObject {} /** A command that has an extension inside of it. */ - public static class ResourceCommandWrapper extends InnerCommand { + @XmlTransient + @XmlAccessorType(XmlAccessType.FIELD) + public abstract static class ResourceCommandWrapper extends InnerCommand { @XmlElementRefs({ @XmlElementRef(type = DomainCommand.Check.class), @XmlElementRef(type = DomainCommand.Create.class), @@ -189,21 +238,65 @@ public class EppInput extends ImmutableObject { } /** Epp envelope wrapper for check on some objects. */ - public static class Check extends ResourceCommandWrapper {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Check extends ResourceCommandWrapper { + public static Check create(ResourceCommand resourceCommand) { + Check instance = new Check(); + instance.resourceCommand = resourceCommand; + return instance; + } + } /** Epp envelope wrapper for create of some object. */ - public static class Create extends ResourceCommandWrapper {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Create extends ResourceCommandWrapper { + public static Create create(ResourceCommand resourceCommand) { + Create instance = new Create(); + instance.resourceCommand = resourceCommand; + return instance; + } + + /** Builder for {@link Create}. */ + public static class Builder extends Buildable.Builder { + public Builder setResourceCommand(ResourceCommand resourceCommand) { + getInstance().resourceCommand = resourceCommand; + return this; + } + } + } /** Epp envelope wrapper for delete of some object. */ - public static class Delete extends ResourceCommandWrapper {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Delete extends ResourceCommandWrapper { + public static Delete create(ResourceCommand resourceCommand) { + Delete instance = new Delete(); + instance.resourceCommand = resourceCommand; + return instance; + } + } /** Epp envelope wrapper for info on some object. */ - public static class Info extends ResourceCommandWrapper {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Info extends ResourceCommandWrapper { + public static Info create(ResourceCommand resourceCommand) { + Info instance = new Info(); + instance.resourceCommand = resourceCommand; + return instance; + } + } /** Epp envelope wrapper for renewing some object. */ - public static class Renew extends ResourceCommandWrapper {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Renew extends ResourceCommandWrapper { + public static Renew create(ResourceCommand resourceCommand) { + Renew instance = new Renew(); + instance.resourceCommand = resourceCommand; + return instance; + } + } /** Epp envelope wrapper for transferring some object. */ + @XmlAccessorType(XmlAccessType.FIELD) public static class Transfer extends ResourceCommandWrapper { /** Enum of the possible values for the "op" attribute in transfer flows. */ @@ -230,12 +323,35 @@ public class EppInput extends ImmutableObject { public TransferOp getTransferOp() { return transferOp; } + + public static Transfer create(TransferOp transferOp, ResourceCommand resourceCommand) { + Transfer instance = new Transfer(); + instance.transferOp = transferOp; + instance.resourceCommand = resourceCommand; + return instance; + } } /** Epp envelope wrapper for update of some object. */ - public static class Update extends ResourceCommandWrapper {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Update extends ResourceCommandWrapper { + public static Update create(ResourceCommand resourceCommand) { + Update instance = new Update(); + instance.resourceCommand = resourceCommand; + return instance; + } + + /** Builder for {@link Update}. */ + public static class Builder extends Buildable.Builder { + public Builder setResourceCommand(ResourceCommand resourceCommand) { + getInstance().resourceCommand = resourceCommand; + return this; + } + } + } /** Poll command. */ + @XmlAccessorType(XmlAccessType.FIELD) public static class Poll extends InnerCommand { /** Enum of the possible values for the "op" attribute in poll commands. */ @@ -253,19 +369,28 @@ public class EppInput extends ImmutableObject { @XmlAttribute PollOp op; - @XmlAttribute - String msgID; + @XmlAttribute(name = "msgID") + String msgId; public PollOp getPollOp() { return op; } public String getMessageId() { - return msgID; + return msgId; + } + + public static Poll create(PollOp op, @Nullable String msgId) { + Poll instance = new Poll(); + instance.op = op; + instance.msgId = msgId; + return instance; } } /** Login command. */ + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"clientId", "password", "newPassword", "options", "services"}) public static class Login extends InnerCommand { @XmlElement(name = "clID") String clientId; @@ -303,10 +428,12 @@ public class EppInput extends ImmutableObject { } /** Logout command. */ + @XmlAccessorType(XmlAccessType.FIELD) public static class Logout extends InnerCommand {} /** The "command" element that holds an actual command inside of it. */ - @XmlType(propOrder = {"command", "extension", "clTRID"}) + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"command", "extension", "clTrid"}) public static class CommandWrapper extends ImmutableObject { @XmlElements({ @XmlElement(name = "check", type = Check.class), @@ -376,7 +503,9 @@ public class EppInput extends ImmutableObject { @XmlElementWrapper List extension; - @Nullable String clTRID; + @XmlElement(name = "clTRID") + @Nullable + String clTrid; /** * Returns the client transaction ID. @@ -384,7 +513,7 @@ public class EppInput extends ImmutableObject { *

This is optional (i.e. it may not be specified) per RFC 5730. */ public Optional getClTrid() { - return Optional.ofNullable(clTRID); + return Optional.ofNullable(clTrid); } public InnerCommand getCommand() { @@ -394,12 +523,34 @@ public class EppInput extends ImmutableObject { public ImmutableList getExtensions() { return nullToEmptyImmutableCopy(extension); } + + /** Builder for {@link CommandWrapper}. */ + public static class Builder extends Buildable.Builder { + + public Builder setCommand(InnerCommand command) { + getInstance().command = command; + return this; + } + + public Builder setExtensions(ImmutableList extension) { + getInstance().extension = isNullOrEmpty(extension) ? null : extension; + return this; + } + + public Builder setClTrid(String clTrid) { + getInstance().clTrid = clTrid; + return this; + } + } } /** Empty type to represent the empty "hello" command. */ + @XmlAccessorType(XmlAccessType.FIELD) public static class Hello extends CommandWrapper {} /** An options object inside of {@link Login}. */ + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"version", "language"}) public static class Options extends ImmutableObject { @XmlJavaTypeAdapter(VersionAdapter.class) String version; @@ -413,6 +564,8 @@ public class EppInput extends ImmutableObject { } /** A services object inside of {@link Login}. */ + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"objectServices", "serviceExtensions"}) public static class Services extends ImmutableObject { @XmlElement(name = "objURI") Set objectServices; @@ -431,15 +584,15 @@ public class EppInput extends ImmutableObject { } /** - * RFC 5730 says we should check the version and return special error code 2100 if it isn't - * what we support, but it also specifies a schema that only allows 1.0 in the version field, so - * any other version doesn't validate. As a result, if we didn't do this here it would throw a - * {@code SyntaxErrorException} when it failed to validate. + * RFC 5730 says we should check the version and return special error code 2100 if it isn't what + * we support, but it also specifies a schema that only allows 1.0 in the version field, so any + * other version doesn't validate. As a result, if we didn't do this here it would throw a {@code + * SyntaxErrorException} when it failed to validate. * - * @see - * RFC 5730 - EPP - Command error responses + * @see RFC 5730 - EPP - Command error + * responses */ - public static class VersionAdapter extends XmlAdapter { + public static class VersionAdapter extends XmlAdapter { @Override public String unmarshal(String version) throws Exception { if (!"1.0".equals(version)) { @@ -449,8 +602,8 @@ public class EppInput extends ImmutableObject { } @Override - public String marshal(String ignored) { - throw new UnsupportedOperationException(); + public String marshal(String version) { + return version; } } diff --git a/core/src/main/java/google/registry/model/eppinput/ResourceCommand.java b/core/src/main/java/google/registry/model/eppinput/ResourceCommand.java index de81fce7d..369204ee4 100644 --- a/core/src/main/java/google/registry/model/eppinput/ResourceCommand.java +++ b/core/src/main/java/google/registry/model/eppinput/ResourceCommand.java @@ -15,7 +15,6 @@ package google.registry.model.eppinput; import static google.registry.util.CollectionUtils.nullSafeImmutableCopy; -import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -24,41 +23,50 @@ import google.registry.model.EppResource; import google.registry.model.ImmutableObject; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; -import google.registry.model.eppinput.ResourceCommand.ResourceUpdate.AddRemove; import google.registry.util.TypeUtils.TypeInstantiator; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlElements; import jakarta.xml.bind.annotation.XmlTransient; import java.util.List; -import java.util.Set; /** Commands for EPP resources. */ public interface ResourceCommand { - /** - * A command for a single {@link EppResource}. - * - *

In general commands should extend {@link AbstractSingleResourceCommand} instead of - * implementing this directly, but "Create" commands can't do that since they need to inherit from - * a base class that gives them all of the resource's fields. The domain "Info" command also can't - * do that since it's "name" field is overloaded with a "hosts" attribute. - */ + /** Interface for EPP commands that operate on a single resource. */ interface SingleResourceCommand extends ResourceCommand { + @Override String getTargetId(); + @Override AuthInfo getAuthInfo(); } + /** Returns the target ID for single-resource commands, or null otherwise. */ + default String getTargetId() { + return null; + } + + /** Returns the auth info for single-resource commands, or null otherwise. */ + default AuthInfo getAuthInfo() { + return null; + } + /** Abstract implementation of {@link ResourceCommand}. */ @XmlTransient - abstract class AbstractSingleResourceCommand extends ImmutableObject + @XmlAccessorType(XmlAccessType.FIELD) + public abstract class AbstractSingleResourceCommand extends ImmutableObject implements SingleResourceCommand { - @XmlElements({ - @XmlElement(name = "id"), - @XmlElement(name = "name") }) - String targetId; + + @XmlTransient public String targetId; + + public void setTargetId(String targetId) { + this.targetId = targetId; + } @Override + @XmlTransient public String getTargetId() { return targetId; } @@ -71,11 +79,14 @@ public interface ResourceCommand { /** A check command for an {@link EppResource}. */ @XmlTransient - class ResourceCheck extends ImmutableObject implements ResourceCommand { - @XmlElements({ - @XmlElement(name = "id"), - @XmlElement(name = "name") }) - List targetUniqueIds; + @XmlAccessorType(XmlAccessType.FIELD) + public class ResourceCheck extends ImmutableObject implements ResourceCommand { + @XmlElements({@XmlElement(name = "id"), @XmlElement(name = "name")}) + public List targetUniqueIds; + + public void setTargetIds(ImmutableList targetUniqueIds) { + this.targetUniqueIds = targetUniqueIds; + } public ImmutableList getTargetIds() { return nullSafeImmutableCopy(targetUniqueIds); @@ -83,7 +94,7 @@ public interface ResourceCommand { } /** A create command, or the inner change (as opposed to add or remove) part of an update. */ - interface ResourceCreateOrChange> {} + public interface ResourceCreateOrChange> {} /** * An update command for an {@link EppResource}. @@ -92,21 +103,19 @@ public interface ResourceCommand { * @param the change type */ @XmlTransient - abstract class ResourceUpdate< - A extends AddRemove, + public abstract class ResourceUpdate< + A extends ResourceUpdate.AddRemove, B extends EppResource.Builder, C extends ResourceCreateOrChange> extends AbstractSingleResourceCommand { /** Part of an update command that specifies set values to add or remove. */ @XmlTransient + @XmlAccessorType(XmlAccessType.FIELD) public abstract static class AddRemove extends ImmutableObject { - @XmlElement(name = "status") - Set statusValues; + public abstract void setStatusValues(ImmutableSet statusValues); - public ImmutableSet getStatusValues() { - return nullToEmptyImmutableCopy(statusValues); - } + public abstract ImmutableSet getStatusValues(); } protected abstract C getNullableInnerChange(); diff --git a/core/src/main/java/google/registry/model/host/HostCommand.java b/core/src/main/java/google/registry/model/host/HostCommand.java index b1baa0f68..2fadaa5e0 100644 --- a/core/src/main/java/google/registry/model/host/HostCommand.java +++ b/core/src/main/java/google/registry/model/host/HostCommand.java @@ -14,14 +14,18 @@ package google.registry.model.host; -import static google.registry.util.CollectionUtils.nullSafeImmutableCopy; +import static google.registry.util.CollectionUtils.isNullOrEmpty; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableSet; +import google.registry.model.Buildable; +import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppinput.ResourceCommand.AbstractSingleResourceCommand; import google.registry.model.eppinput.ResourceCommand.ResourceCheck; import google.registry.model.eppinput.ResourceCommand.ResourceCreateOrChange; import google.registry.model.eppinput.ResourceCommand.ResourceUpdate; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlTransient; @@ -32,39 +36,95 @@ import java.util.Set; /** A collection of {@link Host} commands. */ public class HostCommand { - /** The fields on "chgType" from RFC5732. */ + /** The fields on "chgType" from RFC5732. */ @XmlTransient - abstract static class HostCreateOrChange extends AbstractSingleResourceCommand + @XmlAccessorType(XmlAccessType.FIELD) + public abstract static class HostCreateOrChange extends AbstractSingleResourceCommand implements ResourceCreateOrChange { + + @XmlElement(name = "name") + String name; + + @Override + public String getTargetId() { + return name; + } + + @Override + public void setTargetId(String targetId) { + this.name = targetId; + } + public String getHostName() { - return getTargetId(); + return name; } } /** * A create command for a {@link Host}, mapping "createType" from RFC5732. + * href="https://tools.ietf.org/html/rfc5732">RFC5732. */ - @XmlType(propOrder = {"targetId", "inetAddresses"}) + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"name", "inetAddresses"}) @XmlRootElement - public static class Create extends HostCreateOrChange - implements ResourceCreateOrChange { + public static class Create extends HostCreateOrChange { /** IP Addresses for this host. Can be null if this is an external host. */ @XmlElement(name = "addr") Set inetAddresses; public ImmutableSet getInetAddresses() { - return nullSafeImmutableCopy(inetAddresses); + return nullToEmptyImmutableCopy(inetAddresses); + } + + /** Builder for {@link Create}. */ + public static class Builder extends Buildable.Builder { + public Builder setTargetId(String targetId) { + getInstance().setTargetId(targetId); + return this; + } + + public Builder setInetAddresses(ImmutableSet inetAddresses) { + getInstance().inetAddresses = inetAddresses; + return this; + } } } /** A delete command for a {@link Host}. */ @XmlRootElement - public static class Delete extends AbstractSingleResourceCommand {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Delete extends AbstractSingleResourceCommand { + @XmlElement(name = "name") + String name; + + @Override + public String getTargetId() { + return name; + } + + @Override + public void setTargetId(String targetId) { + this.name = targetId; + } + } /** An info request for a {@link Host}. */ @XmlRootElement - public static class Info extends AbstractSingleResourceCommand {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Info extends AbstractSingleResourceCommand { + @XmlElement(name = "name") + String name; + + @Override + public String getTargetId() { + return name; + } + + @Override + public void setTargetId(String targetId) { + this.name = targetId; + } + } /** A check request for {@link Host}. */ @XmlRootElement @@ -72,17 +132,32 @@ public class HostCommand { /** An update to a {@link Host}. */ @XmlRootElement - @XmlType(propOrder = {"targetId", "innerAdd", "innerRemove", "innerChange"}) - public static class Update extends ResourceUpdate { + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"name", "innerAdd", "innerRemove", "innerChange"}) + public static class Update + extends ResourceUpdate { + + @XmlElement(name = "name") + String name; + + @Override + public String getTargetId() { + return name; + } + + @Override + public void setTargetId(String targetId) { + this.name = targetId; + } @XmlElement(name = "chg") protected Change innerChange; @XmlElement(name = "add") - protected AddRemove innerAdd; + protected HostAddRemove innerAdd; @XmlElement(name = "rem") - protected AddRemove innerRemove; + protected HostAddRemove innerRemove; @Override protected Change getNullableInnerChange() { @@ -90,28 +165,55 @@ public class HostCommand { } @Override - protected AddRemove getNullableInnerAdd() { + protected HostAddRemove getNullableInnerAdd() { return innerAdd; } @Override - protected AddRemove getNullableInnerRemove() { + protected HostAddRemove getNullableInnerRemove() { return innerRemove; } /** The add/remove type on a host update command. */ - @XmlType(propOrder = { "inetAddresses", "statusValues" }) - public static class AddRemove extends ResourceUpdate.AddRemove { + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"inetAddresses", "statusValues"}) + public static class HostAddRemove extends ResourceUpdate.AddRemove { /** IP Addresses for this host. Can be null if this is an external host. */ @XmlElement(name = "addr") Set inetAddresses; + @XmlElement(name = "status") + Set statusValues; + + @Override + public void setStatusValues(ImmutableSet statusValues) { + this.statusValues = statusValues; + } + + @Override + public ImmutableSet getStatusValues() { + return nullToEmptyImmutableCopy(statusValues); + } + public ImmutableSet getInetAddresses() { return nullToEmptyImmutableCopy(inetAddresses); } + + /** Builder for {@link HostAddRemove}. */ + public static class Builder extends Buildable.Builder { + public Builder setInetAddresses(ImmutableSet inetAddresses) { + getInstance().inetAddresses = isNullOrEmpty(inetAddresses) ? null : inetAddresses; + return this; + } + + public Builder setStatusValues(ImmutableSet statusValues) { + getInstance().statusValues = isNullOrEmpty(statusValues) ? null : statusValues; + return this; + } + } } - /** The inner change type on a host update command. */ + @XmlAccessorType(XmlAccessType.FIELD) public static class Change extends HostCreateOrChange {} } } diff --git a/core/src/main/java/google/registry/reporting/spec11/PublishSpec11ReportAction.java b/core/src/main/java/google/registry/reporting/spec11/PublishSpec11ReportAction.java index fe38f03f9..0cf62abaa 100644 --- a/core/src/main/java/google/registry/reporting/spec11/PublishSpec11ReportAction.java +++ b/core/src/main/java/google/registry/reporting/spec11/PublishSpec11ReportAction.java @@ -24,7 +24,6 @@ import static jakarta.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE; import com.google.api.services.dataflow.Dataflow; import com.google.api.services.dataflow.model.Job; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; @@ -32,11 +31,10 @@ import com.google.common.collect.Multimaps; import com.google.common.collect.Sets; import com.google.common.flogger.FluentLogger; import com.google.common.net.MediaType; -import com.google.template.soy.parseinfo.SoyTemplateInfo; import google.registry.beam.spec11.ThreatMatch; import google.registry.config.RegistryConfig.Config; import google.registry.reporting.ReportingModule; -import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo; +import google.registry.reporting.spec11.Spec11EmailUtils.Spec11EmailTemplate; import google.registry.request.Action; import google.registry.request.Parameter; import google.registry.request.Response; @@ -45,14 +43,13 @@ import jakarta.inject.Inject; import java.io.IOException; import java.time.LocalDate; import java.util.Optional; -import java.util.Set; import org.json.JSONException; /** * Retries until a {@code Dataflow} job with a given {@code jobId} completes, continuing the Spec11 * pipeline accordingly. * - *

This calls {@link Spec11EmailUtils#emailSpec11Reports(LocalDate, SoyTemplateInfo, String, + *

This calls {@link Spec11EmailUtils#emailSpec11Reports(LocalDate, Spec11EmailTemplate, String, * ImmutableSet)} on success or {@link Spec11EmailUtils#sendAlertEmail(String, String)} on failure. */ @Action( @@ -134,7 +131,7 @@ public class PublishSpec11ReportAction implements Runnable { String.format("Spec11 %s job %s ended in status failure.", date, jobId)); } default -> { - logger.atInfo().log("Job in non-terminal state %s, retrying:", state); + logger.atInfo().log("Job in non-terminal state %s, retrying.", state); response.setStatus(SC_SERVICE_UNAVAILABLE); } } @@ -153,8 +150,7 @@ public class PublishSpec11ReportAction implements Runnable { ImmutableSet monthlyMatchesSet = spec11RegistrarThreatMatchesParser.getRegistrarThreatMatches(date); String subject = String.format("%s Monthly Threat Detector [%s]", registryName, date); - emailUtils.emailSpec11Reports( - date, Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, subject, monthlyMatchesSet); + emailUtils.emailSpec11Reports(date, Spec11EmailTemplate.MONTHLY, subject, monthlyMatchesSet); } private void processDailyDiff(LocalDate previousDate) throws IOException, JSONException { @@ -165,7 +161,7 @@ public class PublishSpec11ReportAction implements Runnable { String dailySubject = String.format("%s Daily Threat Detector [%s]", registryName, date); emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.DAILY_SPEC_11_EMAIL, + Spec11EmailTemplate.DAILY, dailySubject, getNewMatches(previousMatches, currentMatches)); } @@ -173,19 +169,20 @@ public class PublishSpec11ReportAction implements Runnable { private ImmutableSet getNewMatches( ImmutableSet previousMatchesSet, ImmutableSet currentMatchesSet) { - ImmutableMap> previousMatchesByEmail = + ImmutableMap> previousMatchesByRegistrarId = groupByKeyAndFlatMap(previousMatchesSet); - ImmutableMap> currentMatchesByEmail = + ImmutableMap> currentMatchesByRegistrarId = groupByKeyAndFlatMap(currentMatchesSet); ImmutableSet.Builder resultsBuilder = ImmutableSet.builder(); - for (String email : currentMatchesByEmail.keySet()) { + for (String registrarId : currentMatchesByRegistrarId.keySet()) { // Only include matches in the result if they're non-empty - Set difference = - Sets.difference( - currentMatchesByEmail.get(email), - previousMatchesByEmail.getOrDefault(email, ImmutableSet.of())); + ImmutableSet difference = + ImmutableSet.copyOf( + Sets.difference( + currentMatchesByRegistrarId.get(registrarId), + previousMatchesByRegistrarId.getOrDefault(registrarId, ImmutableSet.of()))); if (!difference.isEmpty()) { - resultsBuilder.add(RegistrarThreatMatches.create(email, ImmutableList.copyOf(difference))); + resultsBuilder.add(RegistrarThreatMatches.create(registrarId, difference.asList())); } } return resultsBuilder.build(); @@ -193,13 +190,13 @@ public class PublishSpec11ReportAction implements Runnable { private ImmutableMap> groupByKeyAndFlatMap( ImmutableSet registrarThreatMatches) { - // Group by email address then flat-map all of the ThreatMatch objects together + // Group by registrarId then flat-map all of the ThreatMatch objects together return ImmutableMap.copyOf( Maps.transformValues( - Multimaps.index(registrarThreatMatches, RegistrarThreatMatches::clientId).asMap(), + Multimaps.index(registrarThreatMatches, RegistrarThreatMatches::registrarId).asMap(), registrarThreatMatchesCollection -> registrarThreatMatchesCollection.stream() - .flatMap(matches -> matches.threatMatches().stream()) + .flatMap(rtm -> rtm.threatMatches().stream()) .collect(toImmutableSet()))); } diff --git a/core/src/main/java/google/registry/reporting/spec11/RegistrarThreatMatches.java b/core/src/main/java/google/registry/reporting/spec11/RegistrarThreatMatches.java index 6ad156339..257cb6aff 100644 --- a/core/src/main/java/google/registry/reporting/spec11/RegistrarThreatMatches.java +++ b/core/src/main/java/google/registry/reporting/spec11/RegistrarThreatMatches.java @@ -16,12 +16,17 @@ package google.registry.reporting.spec11; import com.google.common.collect.ImmutableList; import google.registry.beam.spec11.ThreatMatch; -import java.util.List; -/** Value record representing the registrar and list-of-threat-matches pair stored in GCS. */ -public record RegistrarThreatMatches(String clientId, ImmutableList threatMatches) { - - static RegistrarThreatMatches create(String clientId, List threatMatches) { - return new RegistrarThreatMatches(clientId, ImmutableList.copyOf(threatMatches)); +/** + * A value record representing a registrar and its associated list of threat matches. + * + * @param registrarId the ID of the registrar + * @param threatMatches the list of {@link ThreatMatch} objects associated with the registrar + */ +public record RegistrarThreatMatches(String registrarId, ImmutableList threatMatches) { + /** Creates a new {@link RegistrarThreatMatches} instance. */ + static RegistrarThreatMatches create( + String registrarId, ImmutableList threatMatches) { + return new RegistrarThreatMatches(registrarId, threatMatches); } } diff --git a/core/src/main/java/google/registry/reporting/spec11/Spec11EmailUtils.java b/core/src/main/java/google/registry/reporting/spec11/Spec11EmailUtils.java index 2d105a730..61edcdd5a 100644 --- a/core/src/main/java/google/registry/reporting/spec11/Spec11EmailUtils.java +++ b/core/src/main/java/google/registry/reporting/spec11/Spec11EmailUtils.java @@ -16,7 +16,6 @@ package google.registry.reporting.spec11; import static com.google.common.base.Throwables.getRootCause; import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.io.Resources.getResource; import static google.registry.persistence.transaction.QueryComposer.Comparator; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; @@ -25,41 +24,46 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.flogger.FluentLogger; import com.google.common.net.MediaType; -import com.google.template.soy.SoyFileSet; -import com.google.template.soy.parseinfo.SoyTemplateInfo; -import com.google.template.soy.tofu.SoyTofu; -import com.google.template.soy.tofu.SoyTofu.Renderer; import google.registry.beam.spec11.ThreatMatch; import google.registry.config.RegistryConfig.Config; import google.registry.groups.GmailClient; import google.registry.model.domain.Domain; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarPoc; -import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo; import google.registry.util.EmailMessage; import google.registry.util.Sleeper; +import google.registry.util.TemplateRenderer; import jakarta.inject.Inject; import jakarta.mail.MessagingException; import jakarta.mail.internet.InternetAddress; import java.time.Duration; import java.time.LocalDate; -import java.util.List; import java.util.Map; /** Provides e-mail functionality for Spec11 tasks, such as sending Spec11 reports to registrars. */ public class Spec11EmailUtils { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - private static final SoyTofu SOY_SAUCE = - SoyFileSet.builder() - .add( - getResource( - Spec11EmailSoyInfo.getInstance().getClass(), - Spec11EmailSoyInfo.getInstance().getFileName())) - .build() - .compileToTofu(); + + /** Enum of Spec11 email templates. */ + public enum Spec11EmailTemplate { + DAILY("daily_spec11_email.ftl"), + MONTHLY("monthly_spec11_email.ftl"); + + private final String ftlPath; + + Spec11EmailTemplate(String ftlPath) { + this.ftlPath = "google/registry/reporting/spec11/ftl/" + ftlPath; + } + + public String getFtlPath() { + return ftlPath; + } + } + private final GmailClient gmailClient; private final Sleeper sleeper; + private final TemplateRenderer templateRenderer; private final Duration emailThrottleDuration; private final InternetAddress outgoingEmailAddress; private final ImmutableList spec11BccEmailAddresses; @@ -71,6 +75,7 @@ public class Spec11EmailUtils { Spec11EmailUtils( GmailClient gmailClient, Sleeper sleeper, + TemplateRenderer templateRenderer, @Config("emailThrottleDuration") Duration emailThrottleDuration, @Config("newAlertRecipientEmailAddress") InternetAddress alertRecipientAddress, @Config("spec11OutgoingEmailAddress") InternetAddress spec11OutgoingEmailAddress, @@ -79,6 +84,7 @@ public class Spec11EmailUtils { @Config("registryName") String registryName) { this.gmailClient = gmailClient; this.sleeper = sleeper; + this.templateRenderer = templateRenderer; this.emailThrottleDuration = emailThrottleDuration; this.outgoingEmailAddress = spec11OutgoingEmailAddress; this.spec11BccEmailAddresses = spec11BccEmailAddresses; @@ -88,12 +94,18 @@ public class Spec11EmailUtils { } /** - * Processes a list of registrar/list-of-threat pairings and sends a notification email to the - * appropriate address. + * Processes a list of registrar/list-of-threat pairings and sends notification emails to the + * appropriate addresses. + * + * @param date the date the report was generated + * @param template the email template to use + * @param subject the subject line for the emails + * @param registrarThreatMatchesSet a set of {@link RegistrarThreatMatches} to be emailed + * @throws RuntimeException if emailing fails for one or more registrars */ void emailSpec11Reports( LocalDate date, - SoyTemplateInfo soyTemplateInfo, + Spec11EmailTemplate template, String subject, ImmutableSet registrarThreatMatchesSet) { ImmutableMap.Builder failedMatchesBuilder = @@ -108,14 +120,15 @@ public class Spec11EmailUtils { try { // Handle exceptions individually per registrar so that one failed email doesn't prevent // the rest from being sent. - emailRegistrar(date, soyTemplateInfo, subject, filteredMatches); + emailRegistrar(date, template, subject, filteredMatches); numRegistrarsEmailed++; } catch (Throwable e) { failedMatchesBuilder.put(registrarThreatMatches, getRootCause(e)); } } } - logger.atInfo().log("Emailed daily diffs to %s registrars.", numRegistrarsEmailed); + logger.atInfo().log("Emailed Spec11 reports to %s registrars.", numRegistrarsEmailed); + ImmutableMap failedMatches = failedMatchesBuilder.build(); if (!failedMatches.isEmpty()) { ImmutableList> failedMatchesList = @@ -130,7 +143,7 @@ public class Spec11EmailUtils { logger.atSevere().withCause(failedMatchesList.get(i).getValue()).log( "Additional exception thrown when sending email to registrar %s, in addition to the" + " re-thrown exception.", - failedMatchesList.get(i).getKey().clientId()); + failedMatchesList.get(i).getKey().registrarId()); } throw new RuntimeException( "Emailing Spec11 reports failed, first exception:", firstThrowable); @@ -144,43 +157,49 @@ public class Spec11EmailUtils { RegistrarThreatMatches registrarThreatMatches) { ImmutableList filteredMatches = tm().transact( - () -> { - return registrarThreatMatches.threatMatches().stream() - .filter( - threatMatch -> - tm() - .createQueryComposer(Domain.class) - .where("domainName", Comparator.EQ, threatMatch.domainName()) - .stream() - .anyMatch(Domain::shouldPublishToDns)) - .collect(toImmutableList()); - }); - return RegistrarThreatMatches.create(registrarThreatMatches.clientId(), filteredMatches); + () -> + registrarThreatMatches.threatMatches().stream() + .filter( + threatMatch -> + tm() + .createQueryComposer(Domain.class) + .where("domainName", Comparator.EQ, threatMatch.domainName()) + .stream() + .anyMatch(Domain::shouldPublishToDns)) + .collect(toImmutableList())); + return RegistrarThreatMatches.create(registrarThreatMatches.registrarId(), filteredMatches); } private void emailRegistrar( LocalDate date, - SoyTemplateInfo soyTemplateInfo, + Spec11EmailTemplate template, String subject, RegistrarThreatMatches registrarThreatMatches) throws MessagingException { gmailClient.sendEmail( EmailMessage.newBuilder() .setSubject(subject) - .setBody(getEmailBody(date, soyTemplateInfo, registrarThreatMatches)) + .setBody(getEmailBody(date, template, registrarThreatMatches)) .setContentType(MediaType.HTML_UTF_8) - .addRecipient(getEmailAddressForRegistrar(registrarThreatMatches.clientId())) + .addRecipient(getEmailAddressForRegistrar(registrarThreatMatches.registrarId())) .setBccs(spec11BccEmailAddresses) .build()); } + /** + * Renders the email body using the specified template and registrar threat matches. + * + * @param date the date the report was generated + * @param template the email template to use + * @param registrarThreatMatches the matches for a specific registrar + * @return the rendered email body as an HTML string + */ private String getEmailBody( - LocalDate date, - SoyTemplateInfo soyTemplateInfo, - RegistrarThreatMatches registrarThreatMatches) { - Renderer renderer = SOY_SAUCE.newRenderer(soyTemplateInfo); - // Soy templates require that data be in raw map/list form. - List> threatMatchMap = + LocalDate date, Spec11EmailTemplate template, RegistrarThreatMatches registrarThreatMatches) { + // FreeMarker templates require that data be in raw map/list form or bean-style POJOs. + // We convert the ThreatMatch records to maps here to ensure compatibility and to + // apply email-safe domain name transformations. + ImmutableList> threatMatchMap = registrarThreatMatches.threatMatches().stream() .map( threatMatch -> @@ -189,15 +208,14 @@ public class Spec11EmailUtils { "threatType", threatMatch.threatType())) .collect(toImmutableList()); - Map data = + ImmutableMap data = ImmutableMap.of( "date", date.toString(), "registry", registryName, "replyToEmail", outgoingEmailAddress.getAddress(), "threats", threatMatchMap, "resources", spec11WebResources); - renderer.setData(data); - return renderer.render(); + return templateRenderer.render(template.getFtlPath(), data); } // Mutates a known bad domain to pass spam checks by Email sender and clients, as suggested by diff --git a/core/src/main/java/google/registry/tools/CheckDomainClaimsCommand.java b/core/src/main/java/google/registry/tools/CheckDomainClaimsCommand.java index c1b08d974..27b783252 100644 --- a/core/src/main/java/google/registry/tools/CheckDomainClaimsCommand.java +++ b/core/src/main/java/google/registry/tools/CheckDomainClaimsCommand.java @@ -16,10 +16,12 @@ package google.registry.tools; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; -import com.google.template.soy.data.SoyMapData; import google.registry.config.RegistryConfig.Config; -import google.registry.tools.soy.DomainCheckClaimsSoyInfo; +import google.registry.model.domain.DomainCommand; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; import jakarta.inject.Inject; import java.util.Collection; import java.util.List; @@ -50,11 +52,15 @@ final class CheckDomainClaimsCommand extends NonMutatingEppToolCommand { clientId = registryAdminClientId; } - Multimap domainNameMap = validateAndGroupDomainNamesByTld(mainParameters); + Multimap domainNameMap = + validateAndGroupDomainNamesByTld(ImmutableList.copyOf(mainParameters)); for (Collection values : domainNameMap.asMap().values()) { - setSoyTemplate( - DomainCheckClaimsSoyInfo.getInstance(), DomainCheckClaimsSoyInfo.DOMAINCHECKCLAIMS); - addSoyRecord(clientId, new SoyMapData("domainNames", values)); + DomainCommand.Check checkCommand = new DomainCommand.Check(); + checkCommand.setTargetIds(ImmutableList.copyOf(values)); + addEppInput( + clientId, + EppInput.create(EppInput.Check.create(checkCommand), EppExtensions.launchCheckClaims()) + .withClTrid("RegistryTool")); } } } diff --git a/core/src/main/java/google/registry/tools/CheckDomainCommand.java b/core/src/main/java/google/registry/tools/CheckDomainCommand.java index 76d5f0606..2db44e1a8 100644 --- a/core/src/main/java/google/registry/tools/CheckDomainCommand.java +++ b/core/src/main/java/google/registry/tools/CheckDomainCommand.java @@ -14,14 +14,14 @@ package google.registry.tools; -import static com.google.common.base.Strings.isNullOrEmpty; - import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; -import com.google.template.soy.data.SoyMapData; import google.registry.config.RegistryConfig.Config; -import google.registry.tools.soy.DomainCheckSoyInfo; +import google.registry.model.domain.DomainCommand; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; import jakarta.inject.Inject; import java.util.Collection; import java.util.List; @@ -57,14 +57,20 @@ final class CheckDomainCommand extends NonMutatingEppToolCommand { clientId = registryAdminClientId; } - Multimap domainNameMap = validateAndGroupDomainNamesByTld(mainParameters); + Multimap domainNameMap = + validateAndGroupDomainNamesByTld(ImmutableList.copyOf(mainParameters)); for (Collection values : domainNameMap.asMap().values()) { - setSoyTemplate(DomainCheckSoyInfo.getInstance(), DomainCheckSoyInfo.DOMAINCHECK); - SoyMapData soyMapData = new SoyMapData("domainNames", values); - if (!isNullOrEmpty(allocationToken)) { - soyMapData.put("allocationToken", allocationToken); - } - addSoyRecord(clientId, soyMapData); + ImmutableList domainNames = ImmutableList.copyOf(values); + DomainCommand.Check checkCommand = new DomainCommand.Check(); + checkCommand.setTargetIds(domainNames); + + addEppInput( + clientId, + EppInput.create( + EppInput.Check.create(checkCommand), + EppExtensions.feeCheckCreateV06(domainNames), + EppExtensions.allocationToken(allocationToken)) + .withClTrid("RegistryTool")); } } } diff --git a/core/src/main/java/google/registry/tools/CreateAnchorTenantCommand.java b/core/src/main/java/google/registry/tools/CreateAnchorTenantCommand.java index 626e08f7a..8bde7887e 100644 --- a/core/src/main/java/google/registry/tools/CreateAnchorTenantCommand.java +++ b/core/src/main/java/google/registry/tools/CreateAnchorTenantCommand.java @@ -23,8 +23,12 @@ import static google.registry.util.StringGenerator.DEFAULT_PASSWORD_LENGTH; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.google.common.net.InternetDomainName; -import com.google.template.soy.data.SoyMapData; -import google.registry.tools.soy.CreateAnchorTenantSoyInfo; +import google.registry.model.domain.DomainAuthInfo; +import google.registry.model.domain.DomainCommand; +import google.registry.model.domain.Period; +import google.registry.model.eppcommon.AuthInfo.PasswordAuth; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; import google.registry.util.StringGenerator; import jakarta.inject.Inject; import jakarta.inject.Named; @@ -48,10 +52,12 @@ final class CreateAnchorTenantCommand extends MutatingEppToolCommand { required = true) private String domainName; + @SuppressWarnings("UnusedVariable") @Parameter( names = {"--contact"}, - description = "Contact ID for the request. This will be used for registrant, admin contact, " - + "and tech contact.", + description = + "Contact ID for the request. This will be used for registrant, admin contact, " + + "and tech contact.", required = true) private String contact; @@ -87,15 +93,18 @@ final class CreateAnchorTenantCommand extends MutatingEppToolCommand { cost = getDomainCreateCost(domainName, clock.now(), DEFAULT_ANCHOR_TENANT_PERIOD_YEARS); } - setSoyTemplate(CreateAnchorTenantSoyInfo.getInstance(), - CreateAnchorTenantSoyInfo.CREATEANCHORTENANT); - addSoyRecord(clientId, new SoyMapData( - "domainName", domainName, - "contactId", contact, - "reason", reason, - "password", password, - "period", DEFAULT_ANCHOR_TENANT_PERIOD_YEARS, - "feeCurrency", cost != null ? cost.getCurrencyUnit().toString() : null, - "fee", cost != null ? cost.getAmount().toString() : null)); + DomainCommand.Create.Builder createBuilder = + new DomainCommand.Create.Builder() + .setDomainName(domainName) + .setAuthInfo(DomainAuthInfo.create(PasswordAuth.create(password))) + .setPeriod(Period.create(DEFAULT_ANCHOR_TENANT_PERIOD_YEARS, Period.Unit.YEARS)); + + addEppInput( + clientId, + EppInput.create( + EppInput.Create.create(createBuilder.build()), + EppExtensions.metadata(reason, false, true), + EppExtensions.feeCreateV06(cost)) + .withClTrid("RegistryTool")); } } diff --git a/core/src/main/java/google/registry/tools/CreateDomainCommand.java b/core/src/main/java/google/registry/tools/CreateDomainCommand.java index af4af5121..1c2f59e5c 100644 --- a/core/src/main/java/google/registry/tools/CreateDomainCommand.java +++ b/core/src/main/java/google/registry/tools/CreateDomainCommand.java @@ -20,12 +20,20 @@ import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; -import com.google.template.soy.data.SoyMapData; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedSet; +import google.registry.model.domain.DomainAuthInfo; +import google.registry.model.domain.DomainCommand; +import google.registry.model.domain.Period; +import google.registry.model.eppcommon.AuthInfo.PasswordAuth; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; import google.registry.model.pricing.PremiumPricingEngine.DomainPrices; -import google.registry.tools.soy.DomainCreateSoyInfo; import google.registry.util.StringGenerator; import jakarta.inject.Inject; import jakarta.inject.Named; +import java.math.BigDecimal; +import org.joda.money.CurrencyUnit; import org.joda.money.Money; /** A command to create a new domain via EPP. */ @@ -60,8 +68,8 @@ final class CreateDomainCommand extends CreateOrUpdateDomainCommand { } for (String domain : domains) { - String currency = null; - String cost = null; + CurrencyUnit currency = null; + BigDecimal cost = null; DomainPrices prices = getPricesForDomainName(domain, clock.now()); // Check if the domain is premium and set the fee on the create command if so. @@ -70,29 +78,32 @@ final class CreateDomainCommand extends CreateOrUpdateDomainCommand { !force || forcePremiums, "Forced creates on premium domain(s) require --force_premiums"); Money createCost = prices.getCreateCost(); - currency = createCost.getCurrencyUnit().getCode(); - cost = createCost.multipliedBy(period).getAmount().toString(); + currency = createCost.getCurrencyUnit(); + cost = createCost.multipliedBy(period).getAmount(); printStream.printf( "NOTE: %s is premium at %s per year; sending total cost for %d year(s) of %s %s.\n", domain, createCost, period, currency, cost); } - setSoyTemplate(DomainCreateSoyInfo.getInstance(), DomainCreateSoyInfo.DOMAINCREATE); - SoyMapData soyMapData = - new SoyMapData( - "domain", domain, - "period", period, - "nameservers", nameservers, - "password", password, - "currency", currency, - "price", cost, - "dsRecords", DsRecord.convertToSoy(dsRecords), - "reason", reason, - "allocationToken", allocationToken); - if (requestedByRegistrar != null) { - soyMapData.put("requestedByRegistrar", requestedByRegistrar.toString()); - } - addSoyRecord(clientId, soyMapData); + DomainCommand.Create.Builder createBuilder = + new DomainCommand.Create.Builder() + .setDomainName(domain) + .setAuthInfo(DomainAuthInfo.create(PasswordAuth.create(password))) + .setPeriod(Period.create(period, Period.Unit.YEARS)) + .setNameserverHostNames(ImmutableSortedSet.copyOf(nameservers)); + + addEppInput( + clientId, + EppInput.create( + EppInput.Create.create(createBuilder.build()), + EppExtensions.feeCreate(currency, cost), + EppExtensions.secDnsCreate( + dsRecords.stream() + .map(DsRecord::toDsData) + .collect(ImmutableSet.toImmutableSet())), + EppExtensions.toolMetadata(reason, requestedByRegistrar), + EppExtensions.allocationToken(allocationToken)) + .withClTrid("RegistryTool")); } } } diff --git a/core/src/main/java/google/registry/tools/CreateHostCommand.java b/core/src/main/java/google/registry/tools/CreateHostCommand.java index 55b129b8f..a328902be 100644 --- a/core/src/main/java/google/registry/tools/CreateHostCommand.java +++ b/core/src/main/java/google/registry/tools/CreateHostCommand.java @@ -18,14 +18,14 @@ import static google.registry.util.CollectionUtils.nullToEmpty; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; -import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedSet; import com.google.common.net.InetAddresses; -import com.google.template.soy.data.SoyMapData; -import google.registry.tools.soy.HostCreateSoyInfo; +import google.registry.model.eppinput.EppInput; +import google.registry.model.host.HostCommand; import google.registry.util.DomainNameUtils; -import java.net.Inet4Address; -import java.net.Inet6Address; import java.net.InetAddress; +import java.util.Comparator; import java.util.List; /** A command to create a new host via EPP. */ @@ -52,25 +52,19 @@ final class CreateHostCommand extends MutatingEppToolCommand { @Override protected void initMutatingEppToolCommand() { - setSoyTemplate(HostCreateSoyInfo.getInstance(), HostCreateSoyInfo.HOSTCREATE); - ImmutableList.Builder ipv4Addresses = new ImmutableList.Builder<>(); - ImmutableList.Builder ipv6Addresses = new ImmutableList.Builder<>(); + ImmutableSet.Builder inetAddresses = new ImmutableSet.Builder<>(); for (String address : nullToEmpty(addresses)) { - InetAddress inetAddress = InetAddresses.forString(address); - if (inetAddress instanceof Inet4Address) { - ipv4Addresses.add(inetAddress.getHostAddress()); - } else if (inetAddress instanceof Inet6Address) { - ipv6Addresses.add(inetAddress.getHostAddress()); - } else { - throw new IllegalArgumentException( - String.format("IP address in unknown format: %s", address)); - } + inetAddresses.add(InetAddresses.forString(address)); } - addSoyRecord( + + HostCommand.Create.Builder createBuilder = new HostCommand.Create.Builder(); + createBuilder.setTargetId(DomainNameUtils.canonicalizeHostname(hostName)); + createBuilder.setInetAddresses( + ImmutableSortedSet.copyOf( + Comparator.comparing(InetAddresses::toAddrString), inetAddresses.build())); + + addEppInput( clientId, - new SoyMapData( - "hostname", DomainNameUtils.canonicalizeHostname(hostName), - "ipv4addresses", ipv4Addresses.build(), - "ipv6addresses", ipv6Addresses.build())); + EppInput.create(EppInput.Create.create(createBuilder.build())).withClTrid("RegistryTool")); } } diff --git a/core/src/main/java/google/registry/tools/DeleteDomainCommand.java b/core/src/main/java/google/registry/tools/DeleteDomainCommand.java index da0420241..4be16bd1e 100644 --- a/core/src/main/java/google/registry/tools/DeleteDomainCommand.java +++ b/core/src/main/java/google/registry/tools/DeleteDomainCommand.java @@ -16,8 +16,9 @@ package google.registry.tools; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; -import com.google.template.soy.data.SoyMapData; -import google.registry.tools.soy.DomainDeleteSoyInfo; +import google.registry.model.domain.DomainCommand; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; /** A command to delete a domain via EPP. */ @Parameters(separators = " =", commandDescription = "Delete domain") @@ -60,11 +61,17 @@ final class DeleteDomainCommand extends MutatingEppToolCommand { // Immediate deletion is accomplished using the superuser extension. superuser = true; } - setSoyTemplate(DomainDeleteSoyInfo.getInstance(), DomainDeleteSoyInfo.DELETEDOMAIN); - addSoyRecord(clientId, new SoyMapData( - "domainName", domainName, - "immediately", immediately, - "reason", reason, - "requestedByRegistrar", requestedByRegistrar)); + + DomainCommand.Delete deleteCommand = new DomainCommand.Delete(); + deleteCommand.setTargetId(domainName); + + addEppInput( + clientId, + EppInput.create( + EppInput.Delete.create(deleteCommand), + EppExtensions.toolMetadata( + "Deleted by registry administrator: " + reason, requestedByRegistrar), + EppExtensions.deleteSuperuser(immediately)) + .withClTrid("RegistryTool")); } } diff --git a/core/src/main/java/google/registry/tools/DeleteHostCommand.java b/core/src/main/java/google/registry/tools/DeleteHostCommand.java index 129e68318..ea7231f44 100644 --- a/core/src/main/java/google/registry/tools/DeleteHostCommand.java +++ b/core/src/main/java/google/registry/tools/DeleteHostCommand.java @@ -16,8 +16,9 @@ package google.registry.tools; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; -import com.google.template.soy.data.SoyMapData; -import google.registry.tools.soy.HostDeleteSoyInfo; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; +import google.registry.model.host.HostCommand; import google.registry.util.DomainNameUtils; /** A command to delete a host via EPP. */ @@ -50,12 +51,15 @@ final class DeleteHostCommand extends MutatingEppToolCommand { @Override protected void initMutatingEppToolCommand() { - setSoyTemplate(HostDeleteSoyInfo.getInstance(), HostDeleteSoyInfo.DELETEHOST); - addSoyRecord( + HostCommand.Delete deleteCommand = new HostCommand.Delete(); + deleteCommand.setTargetId(DomainNameUtils.canonicalizeHostname(hostName)); + + addEppInput( clientId, - new SoyMapData( - "hostName", DomainNameUtils.canonicalizeHostname(hostName), - "reason", reason, - "requestedByRegistrar", requestedByRegistrar)); + EppInput.create( + EppInput.Delete.create(deleteCommand), + EppExtensions.toolMetadata( + "Deleted by registry administrator: " + reason, requestedByRegistrar)) + .withClTrid("RegistryTool")); } } diff --git a/core/src/main/java/google/registry/tools/DsRecord.java b/core/src/main/java/google/registry/tools/DsRecord.java index c5582f1c7..caa4c9ead 100644 --- a/core/src/main/java/google/registry/tools/DsRecord.java +++ b/core/src/main/java/google/registry/tools/DsRecord.java @@ -15,7 +15,6 @@ package google.registry.tools; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; import static google.registry.util.PreconditionsUtils.checkArgumentPresent; import com.beust.jcommander.IStringConverter; @@ -23,9 +22,8 @@ import com.google.common.base.Ascii; import com.google.common.base.CharMatcher; import com.google.common.base.Splitter; import com.google.common.io.BaseEncoding; -import com.google.template.soy.data.SoyListData; -import com.google.template.soy.data.SoyMapData; import google.registry.flows.domain.DomainFlowUtils; +import google.registry.model.domain.secdns.DomainDsData; import java.util.List; record DsRecord(int keyTag, int alg, int digestType, String digest) { @@ -76,16 +74,8 @@ record DsRecord(int keyTag, int alg, int digestType, String digest) { elements.get(3)); } - public SoyMapData toSoyData() { - return new SoyMapData( - "keyTag", keyTag(), - "alg", alg(), - "digestType", digestType(), - "digest", digest()); - } - - public static SoyListData convertToSoy(List dsRecords) { - return new SoyListData(dsRecords.stream().map(DsRecord::toSoyData).collect(toImmutableList())); + public DomainDsData toDsData() { + return DomainDsData.create(keyTag(), alg(), digestType(), digest()); } public static class Converter implements IStringConverter { diff --git a/core/src/main/java/google/registry/tools/EppToolCommand.java b/core/src/main/java/google/registry/tools/EppToolCommand.java index 09d6665d1..08b2a9ce2 100644 --- a/core/src/main/java/google/registry/tools/EppToolCommand.java +++ b/core/src/main/java/google/registry/tools/EppToolCommand.java @@ -14,10 +14,8 @@ package google.registry.tools; -import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Strings.nullToEmpty; import static com.google.common.collect.Maps.filterValues; -import static com.google.common.io.Resources.getResource; import static google.registry.model.tld.Tlds.findTldForNameOrThrow; import static google.registry.tools.CommandUtilities.addHeader; import static google.registry.util.DomainNameUtils.canonicalizeHostname; @@ -33,19 +31,16 @@ import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; import com.google.common.net.InternetDomainName; import com.google.common.net.MediaType; -import com.google.template.soy.SoyFileSet; -import com.google.template.soy.data.SoyRecord; -import com.google.template.soy.parseinfo.SoyFileInfo; -import com.google.template.soy.parseinfo.SoyTemplateInfo; +import google.registry.model.eppcommon.EppXmlTransformer; +import google.registry.model.eppinput.EppInput; import google.registry.model.registrar.Registrar; import google.registry.util.Clock; +import google.registry.xml.ValidationMode; import jakarta.inject.Inject; import java.io.IOException; import java.net.URLEncoder; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; /** A command to execute an epp command. */ @@ -58,9 +53,6 @@ abstract class EppToolCommand extends ConfirmingCommand implements CommandWithCo description = "Run in superuser mode") boolean superuser = false; - private SoyFileInfo soyFileInfo; - private SoyTemplateInfo soyRenderer; - private List commands = new ArrayList<>(); private ServiceConnection connection; @@ -74,10 +66,11 @@ abstract class EppToolCommand extends ConfirmingCommand implements CommandWithCo } /** - * Helper function for grouping sets of domain names into respective TLDs. Useful for batched - * EPP calls when invoking commands (i.e. domain check) with sets of domains across multiple TLDs. + * Helper function for grouping sets of domain names into respective TLDs. Useful for batched EPP + * calls when invoking commands (i.e. domain check) with sets of domains across multiple TLDs. */ - protected static Multimap validateAndGroupDomainNamesByTld(List names) { + protected static Multimap validateAndGroupDomainNamesByTld( + ImmutableList names) { ImmutableMultimap.Builder builder = new ImmutableMultimap.Builder<>(); for (String name : names) { String canonicalDomain = canonicalizeHostname(name); @@ -87,11 +80,6 @@ abstract class EppToolCommand extends ConfirmingCommand implements CommandWithCo return builder.build(); } - protected void setSoyTemplate(SoyFileInfo soyFileInfo, SoyTemplateInfo soyRenderer) { - this.soyFileInfo = soyFileInfo; - this.soyRenderer = soyRenderer; - } - @Override public void setConnection(ServiceConnection connection) { this.connection = connection; @@ -103,16 +91,20 @@ abstract class EppToolCommand extends ConfirmingCommand implements CommandWithCo commands.add(new XmlEppParameters(clientId, xml)); } - protected void addSoyRecord(String clientId, SoyRecord record) { - checkNotNull(soyFileInfo, "SoyFileInfo is missing, cannot add record."); - checkNotNull(soyRenderer, "SoyRenderer is missing, cannot add record."); - addXmlCommand(clientId, SoyFileSet.builder() - .add(getResource(soyFileInfo.getClass(), soyFileInfo.getFileName())) - .build() - .compileToTofu() - .newRenderer(soyRenderer) - .setData(record) - .render()); + /** + * Adds an EPP command to the list of commands to be executed. + * + * @param clientId the registrar client ID to execute the command as + * @param eppInput the EPP input object to marshal and send + */ + protected void addEppInput(String clientId, EppInput eppInput) { + try { + String xml = + new String(EppXmlTransformer.marshalInput(eppInput, ValidationMode.STRICT), UTF_8); + addXmlCommand(clientId, xml); + } catch (Exception e) { + throw new RuntimeException("Failed to marshal EppInput", e); + } } /** Subclasses can override to implement a dry run flag. False by default. */ @@ -133,21 +125,23 @@ abstract class EppToolCommand extends ConfirmingCommand implements CommandWithCo return prompt; } - private List processCommands(boolean dryRun) throws IOException { + private ImmutableList processCommands(boolean dryRun) throws IOException { ImmutableList.Builder responses = new ImmutableList.Builder<>(); for (XmlEppParameters command : commands) { - Map params = new HashMap<>(); - params.put("dryRun", dryRun); - params.put("clientId", command.clientId); - params.put("superuser", superuser); - params.put("xml", URLEncoder.encode(command.xml, UTF_8)); + ImmutableMap params = + ImmutableMap.builder() + .put("dryRun", dryRun) + .put("clientId", command.clientId) + .put("superuser", superuser) + .put("xml", URLEncoder.encode(command.xml, UTF_8)) + .build(); String requestBody = Joiner.on('&').withKeyValueSeparator("=").join(filterValues(params, Objects::nonNull)); responses.add( nullToEmpty( connection.sendPostRequest( "/_dr/epptool", - ImmutableMap.of(), + ImmutableMap.of(), MediaType.FORM_DATA, requestBody.getBytes(UTF_8)))); } diff --git a/core/src/main/java/google/registry/tools/RenewDomainCommand.java b/core/src/main/java/google/registry/tools/RenewDomainCommand.java index da7b8b707..e46ef0007 100644 --- a/core/src/main/java/google/registry/tools/RenewDomainCommand.java +++ b/core/src/main/java/google/registry/tools/RenewDomainCommand.java @@ -18,18 +18,20 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Strings.isNullOrEmpty; import static google.registry.util.CollectionUtils.findDuplicates; import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; +import static java.time.ZoneOffset.UTC; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.google.common.base.Joiner; -import com.google.template.soy.data.SoyMapData; import google.registry.flows.ResourceFlowUtils; import google.registry.model.domain.Domain; -import google.registry.tools.soy.DomainRenewSoyInfo; +import google.registry.model.domain.DomainCommand; +import google.registry.model.domain.Period; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; import java.time.Instant; -import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; import java.util.List; +import java.util.Set; /** A command to renew domain(s) via EPP. */ @Parameters(separators = " =", commandDescription = "Renew domain(s) via EPP.") @@ -61,35 +63,36 @@ final class RenewDomainCommand extends MutatingEppToolCommand { arity = 1) Boolean requestedByRegistrar; - private static final DateTimeFormatter DATE_FORMATTER = - DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneOffset.UTC); - @Override - protected void initMutatingEppToolCommand() - throws ResourceFlowUtils.ResourceDoesNotExistException { - String duplicates = Joiner.on(", ").join(findDuplicates(mainParameters)); - checkArgument(duplicates.isEmpty(), "Duplicate domain arguments found: '%s'", duplicates); + protected void initMutatingEppToolCommand() throws Exception { + Set duplicates = findDuplicates(mainParameters); + checkArgument( + duplicates.isEmpty(), + "Duplicate domain arguments found: '%s'", + Joiner.on(", ").join(duplicates)); checkArgument(period < 10, "Cannot renew domains for 10 or more years"); Instant now = clock.now(); for (String domainName : mainParameters) { Domain domain = ResourceFlowUtils.loadAndVerifyExistence(Domain.class, domainName, now); - setSoyTemplate(DomainRenewSoyInfo.getInstance(), DomainRenewSoyInfo.RENEWDOMAIN); - SoyMapData soyMapData = - new SoyMapData( - "domainName", domain.getDomainName(), - "expirationDate", DATE_FORMATTER.format(domain.getRegistrationExpirationTime()), - "period", String.valueOf(period)); - if (requestedByRegistrar != null) { - soyMapData.put("requestedByRegistrar", requestedByRegistrar.toString()); - } if (reason != null) { checkArgumentNotNull( requestedByRegistrar, "--registrar_request is required when --reason is specified"); - soyMapData.put("reason", reason); } - addSoyRecord( - isNullOrEmpty(clientId) ? domain.getCurrentSponsorRegistrarId() : clientId, soyMapData); + + DomainCommand.Renew.Builder renewBuilder = + new DomainCommand.Renew.Builder() + .setTargetId(domain.getDomainName()) + .setPeriod(Period.create(period, Period.Unit.YEARS)) + .setCurrentExpirationDate( + domain.getRegistrationExpirationTime().atZone(UTC).toLocalDate()); + + addEppInput( + isNullOrEmpty(clientId) ? domain.getCurrentSponsorRegistrarId() : clientId, + EppInput.create( + EppInput.Renew.create(renewBuilder.build()), + EppExtensions.toolMetadata(reason, requestedByRegistrar)) + .withClTrid("RegistryTool")); } } } diff --git a/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java b/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java index df176a7e2..145b3e73c 100644 --- a/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java +++ b/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java @@ -15,7 +15,7 @@ package google.registry.tools; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.Sets.difference; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static java.time.ZoneOffset.UTC; @@ -27,36 +27,38 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; -import com.google.template.soy.data.SoyListData; -import com.google.template.soy.data.SoyMapData; import google.registry.flows.ResourceFlowUtils; import google.registry.model.ForeignKeyUtils; import google.registry.model.domain.Domain; +import google.registry.model.domain.DomainCommand; +import google.registry.model.domain.Period; import google.registry.model.domain.secdns.DomainDsData; import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; import google.registry.model.host.Host; import google.registry.tools.params.NameserversParameter; -import google.registry.tools.soy.DomainRenewSoyInfo; -import google.registry.tools.soy.UniformRapidSuspensionSoyInfo; import jakarta.xml.bind.annotation.adapters.HexBinaryAdapter; import java.time.Instant; -import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; /** A command to suspend a domain for the Uniform Rapid Suspension process. */ -@Parameters(separators = " =", +@Parameters( + separators = " =", commandDescription = "Suspend a domain for Uniform Rapid Suspension.") final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { - private static final ImmutableSet URS_LOCKS = ImmutableSet.of( - StatusValue.SERVER_DELETE_PROHIBITED.getXmlName(), - StatusValue.SERVER_TRANSFER_PROHIBITED.getXmlName(), - StatusValue.SERVER_UPDATE_PROHIBITED.getXmlName()); + private static final ImmutableSet URS_LOCKS = + ImmutableSet.of( + StatusValue.SERVER_DELETE_PROHIBITED.getXmlName(), + StatusValue.SERVER_TRANSFER_PROHIBITED.getXmlName(), + StatusValue.SERVER_UPDATE_PROHIBITED.getXmlName()); - /** Client id that made this change. Only recorded in the history entry. **/ + /** Client id that made this change. Only recorded in the history entry. * */ private static final String CLIENT_ID = "CharlestonRoad"; @Parameter( @@ -127,12 +129,12 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { superuser = true; Instant now = clock.now(); Domain domain = ResourceFlowUtils.loadAndVerifyExistence(Domain.class, domainName, now); - Set missingHosts = - difference(newHosts, ForeignKeyUtils.loadKeys(Host.class, newHosts, now).keySet()); + ImmutableSet missingHosts = + ImmutableSet.copyOf( + difference(newHosts, ForeignKeyUtils.loadKeys(Host.class, newHosts, now).keySet())); checkArgument(missingHosts.isEmpty(), "Hosts do not exist: %s", missingHosts); checkArgument( - locksToPreserve.isEmpty() || undo, - "Locks can only be preserved when running with --undo"); + locksToPreserve.isEmpty() || undo, "Locks can only be preserved when running with --undo"); existingNameservers = getExistingNameservers(domain); existingLocks = getExistingLocks(domain); existingDsData = getExistingDsData(domain); @@ -152,54 +154,92 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { // trigger renew flow if (renewOneYear) { - setSoyTemplate(DomainRenewSoyInfo.getInstance(), DomainRenewSoyInfo.RENEWDOMAIN); - addSoyRecord( + DomainCommand.Renew.Builder renewBuilder = + new DomainCommand.Renew.Builder() + .setTargetId(domain.getDomainName()) + .setPeriod(Period.create(1, Period.Unit.YEARS)) + .setCurrentExpirationDate( + domain.getRegistrationExpirationTime().atZone(UTC).toLocalDate()); + + addEppInput( CLIENT_ID, - new SoyMapData( - "domainName", - domain.getDomainName(), - "expirationDate", - DateTimeFormatter.ofPattern("yyyy-MM-dd") - .withZone(UTC) - .format(domain.getRegistrationExpirationTime()), - // period is the number of years to renew the registration for - "period", - String.valueOf(1), - // use the same values for reason and requestedByRegistrar from update flow - "reason", - (undo ? "Undo " : "") + "Uniform Rapid Suspension", - "requestedByRegistrar", - Boolean.toString(false))); + EppInput.create( + EppInput.Renew.create(renewBuilder.build()), + EppExtensions.toolMetadata( + (undo ? "Undo " : "") + "Uniform Rapid Suspension", false)) + .withClTrid("RegistryTool")); } // trigger update flow - setSoyTemplate( - UniformRapidSuspensionSoyInfo.getInstance(), - UniformRapidSuspensionSoyInfo.UNIFORMRAPIDSUSPENSION); - addSoyRecord( + DomainCommand.Update.Builder updateBuilder = + new DomainCommand.Update.Builder().setTargetId(domainName); + + DomainCommand.Update.DomainAddRemove.Builder addBuilder = + new DomainCommand.Update.DomainAddRemove.Builder(); + DomainCommand.Update.DomainAddRemove.Builder removeBuilder = + new DomainCommand.Update.DomainAddRemove.Builder(); + boolean hasAdd = false; + boolean hasRemove = false; + + if (!statusesToApply.isEmpty()) { + addBuilder.setStatusValues( + statusesToApply.stream() + .map(StatusValue::fromXmlName) + .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.naturalOrder()))); + hasAdd = true; + } + + ImmutableSet statusesToRemove = + undo + ? ImmutableSet.copyOf(difference(URS_LOCKS, ImmutableSet.copyOf(locksToPreserve))) + : removeStatuses; + + if (!statusesToRemove.isEmpty()) { + removeBuilder.setStatusValues( + statusesToRemove.stream() + .map(StatusValue::fromXmlName) + .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.naturalOrder()))); + hasRemove = true; + } + + ImmutableSet addNameservers = + ImmutableSet.copyOf(difference(newHosts, existingNameservers)); + if (!addNameservers.isEmpty()) { + addBuilder.setNameserverHostNames(ImmutableSortedSet.copyOf(addNameservers)); + hasAdd = true; + } + + ImmutableSet removeNameservers = + ImmutableSet.copyOf(difference(existingNameservers, newHosts)); + if (!removeNameservers.isEmpty()) { + removeBuilder.setNameserverHostNames(ImmutableSortedSet.copyOf(removeNameservers)); + hasRemove = true; + } + + if (hasAdd) { + updateBuilder.setInnerAdd(addBuilder.build()); + } + if (hasRemove) { + updateBuilder.setInnerRemove(removeBuilder.build()); + } + + addEppInput( CLIENT_ID, - new SoyMapData( - "domainName", - domainName, - "hostsToAdd", - difference(newHosts, existingNameservers), - "hostsToRemove", - difference(existingNameservers, newHosts), - "statusesToApply", - statusesToApply, - "statusesToRemove", - undo ? difference(URS_LOCKS, ImmutableSet.copyOf(locksToPreserve)) : removeStatuses, - "newDsData", - newDsData != null ? DsRecord.convertToSoy(newDsData) : new SoyListData(), - "reason", - (undo ? "Undo " : "") + "Uniform Rapid Suspension", - // Domain auto-renewal is disabled as part of URS, and it's re-enabled if URS is undone. - // Therefore, autorenews is set to false by default and it's set to true only if the - // command is run in --undo mode. - "autorenews", - Boolean.toString(undo))); + EppInput.create( + EppInput.Update.create(updateBuilder.build()), + EppExtensions.secDnsUpdate( + newDsData == null + ? ImmutableSet.of() + : newDsData.stream().map(DsRecord::toDsData).collect(toImmutableSet()), + ImmutableSet.of(), + true), + EppExtensions.updateSuperuser(undo), + EppExtensions.toolMetadata( + (undo ? "Undo " : "") + "Uniform Rapid Suspension", false)) + .withClTrid("RegistryTool")); } + /** Returns the set of existing nameservers for the specified domain. */ private ImmutableSortedSet getExistingNameservers(Domain domain) { ImmutableSortedSet.Builder nameservers = ImmutableSortedSet.naturalOrder(); for (Host host : tm().transact(() -> tm().loadByKeys(domain.getNameservers()).values())) { @@ -208,6 +248,7 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { return nameservers.build(); } + /** Returns the set of existing URS-related locks for the specified domain. */ private ImmutableSortedSet getExistingLocks(Domain domain) { ImmutableSortedSet.Builder locks = ImmutableSortedSet.naturalOrder(); for (StatusValue lock : domain.getStatusValues()) { @@ -218,6 +259,7 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { return locks.build(); } + /** Returns whether the specified domain has a CLIENT_HOLD status. */ private boolean hasClientHold(Domain domain) { for (StatusValue status : domain.getStatusValues()) { if (status == StatusValue.CLIENT_HOLD) { @@ -227,6 +269,7 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { return false; } + /** Returns a list of the existing DS records for the specified domain as JSON-like maps. */ private ImmutableList> getExistingDsData(Domain domain) { ImmutableList.Builder> dsDataJsons = new ImmutableList.Builder(); HexBinaryAdapter hexBinaryAdapter = new HexBinaryAdapter(); @@ -273,7 +316,7 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { rec.get("digestType"), rec.get("digest"))) .sorted() - .collect(toImmutableList()); + .collect(ImmutableList.toImmutableList()); undoBuilder.append(" --dsdata ").append(Joiner.on(',').join(formattedDsRecords)); } return undoBuilder.toString(); diff --git a/core/src/main/java/google/registry/tools/UpdateDomainCommand.java b/core/src/main/java/google/registry/tools/UpdateDomainCommand.java index 16be3b90c..b88298295 100644 --- a/core/src/main/java/google/registry/tools/UpdateDomainCommand.java +++ b/core/src/main/java/google/registry/tools/UpdateDomainCommand.java @@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static google.registry.model.domain.rgp.GracePeriodStatus.AUTO_RENEW; import static google.registry.model.eppcommon.StatusValue.PENDING_DELETE; import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED; +import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import static java.util.function.Predicate.isEqual; import com.beust.jcommander.Parameter; @@ -26,19 +27,23 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Sets; import com.google.common.flogger.FluentLogger; -import com.google.template.soy.data.SoyMapData; import google.registry.flows.ResourceFlowUtils; import google.registry.model.domain.Domain; +import google.registry.model.domain.DomainAuthInfo; +import google.registry.model.domain.DomainCommand; import google.registry.model.domain.GracePeriodBase; +import google.registry.model.domain.secdns.SecDnsUpdateExtension; +import google.registry.model.eppcommon.AuthInfo.PasswordAuth; import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; import google.registry.tools.params.NameserversParameter; -import google.registry.tools.soy.DomainUpdateSoyInfo; import java.time.Instant; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.TreeSet; import javax.annotation.Nullable; /** A command to update a new domain via EPP. */ @@ -60,9 +65,8 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { private Set addNameservers = new HashSet<>(); @Parameter( - names = "--add_statuses", - description = "Statuses to add. Cannot be set if --statuses is set." - ) + names = "--add_statuses", + description = "Statuses to add. Cannot be set if --statuses is set.") private List addStatuses = new ArrayList<>(); @Parameter( @@ -82,9 +86,8 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { private Set removeNameservers = new HashSet<>(); @Parameter( - names = "--remove_statuses", - description = "Statuses to remove. Cannot be set if --statuses is set." - ) + names = "--remove_statuses", + description = "Statuses to remove. Cannot be set if --statuses is set.") private List removeStatuses = new ArrayList<>(); @Parameter( @@ -95,10 +98,8 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { private List removeDsRecords = new ArrayList<>(); @Parameter( - names = "--clear_ds_records", - description = - "removes all DS records. Is implied true if --ds_records is set." - ) + names = "--clear_ds_records", + description = "removes all DS records. Is implied true if --ds_records is set.") boolean clearDsRecords = false; @Nullable @@ -133,7 +134,7 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { + "you cannot use the add_statuses and remove_statuses flags."); } - if (!dsRecords.isEmpty() || clearDsRecords){ + if (!dsRecords.isEmpty() || clearDsRecords) { checkArgument( addDsRecords.isEmpty() && removeDsRecords.isEmpty(), "If you provide the ds_records or clear_ds_records flags, " @@ -146,6 +147,12 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { Instant now = clock.now(); for (String domainName : domains) { Domain domain = ResourceFlowUtils.loadAndVerifyExistence(Domain.class, domainName, now); + + if (reason != null) { + checkArgumentNotNull( + requestedByRegistrar, "--registrar_request is required when --reason is specified"); + } + checkArgument( !domain.getStatusValues().contains(SERVER_UPDATE_PROHIBITED), "The domain '%s' has status SERVER_UPDATE_PROHIBITED. Verify that you are allowed " @@ -158,61 +165,18 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { + "--force_in_pending_delete parameter to allow this update.", domainName); - // Use TreeSets so that the results are always in the same order (this makes testing easier). - Set addNameserversThisDomain = new TreeSet<>(addNameservers); - Set removeNameserversThisDomain = new TreeSet<>(removeNameservers); - Set addStatusesThisDomain = new TreeSet<>(addStatuses); - Set removeStatusesThisDomain = new TreeSet<>(removeStatuses); + if (!nameservers.isEmpty()) { + ImmutableSortedSet existingNameservers = domain.loadNameserverHostNames(); + ImmutableSet targetNameservers = ImmutableSet.copyOf(nameservers); - if (!nameservers.isEmpty() || !statuses.isEmpty()) { - if (!nameservers.isEmpty()) { - ImmutableSortedSet existingNameservers = domain.loadNameserverHostNames(); - populateAddRemoveLists( - ImmutableSet.copyOf(nameservers), - existingNameservers, - addNameserversThisDomain, - removeNameserversThisDomain); - int numNameservers = - existingNameservers.size() - + addNameserversThisDomain.size() - - removeNameserversThisDomain.size(); - checkArgument( - numNameservers <= 13, - "The resulting nameservers count for domain %s would be more than 13", - domainName); - } - - if (!statuses.isEmpty()) { - Set currentStatusValues = new HashSet<>(); - for (StatusValue statusValue : domain.getStatusValues()) { - currentStatusValues.add(statusValue.getXmlName()); - } - populateAddRemoveLists( - ImmutableSet.copyOf(statuses), - currentStatusValues, - addStatusesThisDomain, - removeStatusesThisDomain); - } - } - - boolean add = - (!addNameserversThisDomain.isEmpty() - || !addStatusesThisDomain.isEmpty()); - - boolean remove = - (!removeNameserversThisDomain.isEmpty() - || !removeStatusesThisDomain.isEmpty()); - - boolean change = password != null; - boolean secDns = - (!addDsRecords.isEmpty() - || !removeDsRecords.isEmpty() - || !dsRecords.isEmpty() - || clearDsRecords); - - if (!add && !remove && !change && !secDns && autorenews == null) { - logger.atInfo().log("No changes need to be made to domain '%s'.", domainName); - continue; + int numNameservers = + existingNameservers.size() + + Sets.difference(targetNameservers, existingNameservers).size() + - Sets.difference(existingNameservers, targetNameservers).size(); + checkArgument( + numNameservers <= 13, + "The resulting nameservers count for domain %s would be more than 13", + domainName); } // If autorenew is being turned off and this domain is already in the autorenew grace period, @@ -225,30 +189,121 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { } } - setSoyTemplate(DomainUpdateSoyInfo.getInstance(), DomainUpdateSoyInfo.DOMAINUPDATE); - SoyMapData soyMapData = - new SoyMapData( - "domain", domainName, - "add", add, - "addNameservers", addNameserversThisDomain, - "addStatuses", addStatusesThisDomain, - "remove", remove, - "removeNameservers", removeNameserversThisDomain, - "removeStatuses", removeStatusesThisDomain, - "change", change, - "password", password, - "secdns", secDns, - "addDsRecords", DsRecord.convertToSoy(addDsRecords), - "removeDsRecords", DsRecord.convertToSoy(removeDsRecords), - "removeAllDsRecords", clearDsRecords, - "reason", reason); - if (autorenews != null) { - soyMapData.put("autorenews", autorenews.toString()); + DomainCommand.Update.Builder updateBuilder = + new DomainCommand.Update.Builder().setTargetId(domainName); + DomainCommand.Update.DomainAddRemove.Builder addBuilder = + new DomainCommand.Update.DomainAddRemove.Builder(); + DomainCommand.Update.DomainAddRemove.Builder removeBuilder = + new DomainCommand.Update.DomainAddRemove.Builder(); + + boolean hasAdd = false; + boolean hasRemove = false; + boolean hasChange = false; + + if (!nameservers.isEmpty()) { + ImmutableSortedSet current = domain.loadNameserverHostNames(); + ImmutableSet target = ImmutableSet.copyOf(nameservers); + ImmutableSortedSet toAdd = + ImmutableSortedSet.copyOf(Sets.difference(target, current)); + ImmutableSortedSet toRemove = + ImmutableSortedSet.copyOf(Sets.difference(current, target)); + if (!toAdd.isEmpty()) { + addBuilder.setNameserverHostNames(toAdd); + hasAdd = true; + } + if (!toRemove.isEmpty()) { + removeBuilder.setNameserverHostNames(toRemove); + hasRemove = true; + } + } else { + if (!addNameservers.isEmpty()) { + addBuilder.setNameserverHostNames(ImmutableSortedSet.copyOf(addNameservers)); + hasAdd = true; + } + if (!removeNameservers.isEmpty()) { + removeBuilder.setNameserverHostNames(ImmutableSortedSet.copyOf(removeNameservers)); + hasRemove = true; + } } - if (requestedByRegistrar != null) { - soyMapData.put("requestedByRegistrar", requestedByRegistrar.toString()); + + if (!statuses.isEmpty()) { + ImmutableSortedSet current = + ImmutableSortedSet.copyOf(domain.getStatusValues()); + ImmutableSet target = + statuses.stream().map(StatusValue::fromXmlName).collect(ImmutableSet.toImmutableSet()); + ImmutableSortedSet toAdd = + ImmutableSortedSet.copyOf(Sets.difference(target, current)); + ImmutableSortedSet toRemove = + ImmutableSortedSet.copyOf(Sets.difference(current, target)); + if (!toAdd.isEmpty()) { + addBuilder.setStatusValues(toAdd); + hasAdd = true; + } + if (!toRemove.isEmpty()) { + removeBuilder.setStatusValues(toRemove); + hasRemove = true; + } + } else { + if (!addStatuses.isEmpty()) { + addBuilder.setStatusValues( + addStatuses.stream() + .map(StatusValue::fromXmlName) + .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.naturalOrder()))); + hasAdd = true; + } + if (!removeStatuses.isEmpty()) { + removeBuilder.setStatusValues( + removeStatuses.stream() + .map(StatusValue::fromXmlName) + .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.naturalOrder()))); + hasRemove = true; + } + } + + if (hasAdd) { + updateBuilder.setInnerAdd(addBuilder.build()); + } + if (hasRemove) { + updateBuilder.setInnerRemove(removeBuilder.build()); + } + + if (password != null) { + updateBuilder.setInnerChange( + new DomainCommand.Update.Change.Builder() + .setAuthInfo(DomainAuthInfo.create(PasswordAuth.create(password))) + .build()); + hasChange = true; + } + + SecDnsUpdateExtension secDnsUpdate = null; + if (!addDsRecords.isEmpty() + || !removeDsRecords.isEmpty() + || !dsRecords.isEmpty() + || clearDsRecords) { + secDnsUpdate = + EppExtensions.secDnsUpdate( + addDsRecords.stream() + .map(DsRecord::toDsData) + .collect(ImmutableSet.toImmutableSet()), + removeDsRecords.stream() + .map(DsRecord::toDsData) + .collect(ImmutableSet.toImmutableSet()), + clearDsRecords); + } + + if (hasAdd || hasRemove || hasChange || secDnsUpdate != null || autorenews != null) { + addEppInput( + clientId, + EppInput.create( + EppInput.Update.create(updateBuilder.build()), + EppExtensions.updateSuperuser(autorenews), + EppExtensions.toolMetadata(reason, requestedByRegistrar), + secDnsUpdate) + .withClTrid("RegistryTool")); + } else { + logger.atInfo().log( + "Skipping domain '%s' because there are no changes to make.", domainName); } - addSoyRecord(clientId, soyMapData); } ImmutableSet domainsToWarn = autorenewGracePeriodWarningDomains.build(); @@ -260,10 +315,4 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { String.join(", ", domainsToWarn)); } } - - private void populateAddRemoveLists( - Set targetSet, Set oldSet, Set addSet, Set removeSet) { - addSet.addAll(Sets.difference(targetSet, oldSet)); - removeSet.addAll(Sets.difference(oldSet, targetSet)); - } } diff --git a/core/src/main/java/google/registry/tools/UpdateServerLocksCommand.java b/core/src/main/java/google/registry/tools/UpdateServerLocksCommand.java index 6c00ad297..a5706a558 100644 --- a/core/src/main/java/google/registry/tools/UpdateServerLocksCommand.java +++ b/core/src/main/java/google/registry/tools/UpdateServerLocksCommand.java @@ -21,94 +21,119 @@ import static com.google.common.collect.Sets.intersection; import static com.google.common.collect.Sets.union; import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParameterException; import com.beust.jcommander.Parameters; import com.google.common.collect.ImmutableSet; -import com.google.template.soy.data.SoyMapData; +import com.google.common.collect.ImmutableSortedSet; +import google.registry.model.domain.DomainCommand; import google.registry.model.eppcommon.StatusValue; -import google.registry.tools.soy.UpdateServerLocksSoyInfo; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; -import java.util.Set; /** A command to execute a domain check claims epp command. */ @Parameters(separators = " =", commandDescription = "Toggle server locks on a domain.") final class UpdateServerLocksCommand extends MutatingEppToolCommand { - @Parameter( - names = {"-c", "--client"}, - description = "Client identifier of the registrar to execute the command as", - required = true) - String clientId; - @Parameter( names = {"-n", "--domain_name"}, - description = "Domain to modify.", + description = "Domain to lock/unlock.", required = true) private String domainName; @Parameter( - names = {"-a", "--apply"}, - description = "Comma-delimited set of locks to apply (or 'all'). " - + "Valid locks: serverDeleteProhibited, serverHold, serverRenewProhibited, " - + "serverTransferProhibited, serverUpdateProhibited") - private List locksToApply = new ArrayList<>(); - - @Parameter( - names = {"-r", "--remove"}, - description = "Comma-delimited set of locks to remove (or 'all'). " - + "Valid locks: same as for 'apply'.") - private List locksToRemove = new ArrayList<>(); + names = {"--client"}, + description = "Client ID to use for the EPP command.", + required = true) + private String clientId; @Parameter( names = {"--reason"}, - description = "Reason for the change. Required if registrar_request = false.") + description = "Reason for the change.") private String reason; + @Parameter( + names = {"--apply"}, + description = "Statuses to apply. Use \"all\" to apply all server locks.") + private List locksToApply = new ArrayList<>(); + + @Parameter( + names = {"--remove"}, + description = "Statuses to remove. Use \"all\" to remove all server locks.") + private List locksToRemove = new ArrayList<>(); + @Parameter( names = {"--registrar_request"}, description = "Whether the change was requested by a registrar.", - required = true, arity = 1) - private boolean requestedByRegistrar; + private Boolean requestedByRegistrar; - private static final ImmutableSet ALLOWED_VALUES = ImmutableSet.of( - StatusValue.SERVER_DELETE_PROHIBITED.getXmlName(), - StatusValue.SERVER_HOLD.getXmlName(), - StatusValue.SERVER_RENEW_PROHIBITED.getXmlName(), - StatusValue.SERVER_TRANSFER_PROHIBITED.getXmlName(), - StatusValue.SERVER_UPDATE_PROHIBITED.getXmlName()); + private static final ImmutableSet ALLOWED_VALUES = + ImmutableSet.of( + StatusValue.SERVER_DELETE_PROHIBITED.getXmlName(), + StatusValue.SERVER_HOLD.getXmlName(), + StatusValue.SERVER_RENEW_PROHIBITED.getXmlName(), + StatusValue.SERVER_TRANSFER_PROHIBITED.getXmlName(), + StatusValue.SERVER_UPDATE_PROHIBITED.getXmlName()); - private static Set getStatusValuesSet(List statusValues) { - Set statusValuesSet = ImmutableSet.copyOf(statusValues); + private static ImmutableSet getStatusValuesSet(List statusValues) { + ImmutableSet statusValuesSet = ImmutableSet.copyOf(statusValues); if (statusValuesSet.contains("all")) { return ALLOWED_VALUES; } - Set badValues = difference(statusValuesSet, ALLOWED_VALUES); + ImmutableSet badValues = + ImmutableSet.copyOf(difference(statusValuesSet, ALLOWED_VALUES)); checkArgument(badValues.isEmpty(), "Invalid status values: %s", badValues); return statusValuesSet; } @Override protected void initMutatingEppToolCommand() { + if (requestedByRegistrar == null) { + throw new ParameterException("--registrar_request must be specified"); + } checkArgument( requestedByRegistrar || !isNullOrEmpty(reason), "A reason must be provided when a change is not requested by a registrar."); - Set valuesToApply = getStatusValuesSet(locksToApply); - Set valuesToRemove = getStatusValuesSet(locksToRemove); + ImmutableSet valuesToApply = getStatusValuesSet(locksToApply); + ImmutableSet valuesToRemove = getStatusValuesSet(locksToRemove); checkArgument( intersection(valuesToApply, valuesToRemove).isEmpty(), "Add and remove actions overlap"); checkArgument( !union(valuesToApply, valuesToRemove).isEmpty(), "Add and remove actions are both empty"); - setSoyTemplate( - UpdateServerLocksSoyInfo.getInstance(), UpdateServerLocksSoyInfo.UPDATESERVERLOCKS); - addSoyRecord(clientId, new SoyMapData( - "domainName", domainName, - "locksToApply", valuesToApply, - "locksToRemove", valuesToRemove, - "reason", reason, - "requestedByRegistrar", requestedByRegistrar)); + + DomainCommand.Update.Builder updateBuilder = + new DomainCommand.Update.Builder().setTargetId(domainName); + + if (!valuesToApply.isEmpty()) { + updateBuilder.setInnerAdd( + new DomainCommand.Update.DomainAddRemove.Builder() + .setStatusValues( + valuesToApply.stream() + .map(StatusValue::fromXmlName) + .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.naturalOrder()))) + .build()); + } + if (!valuesToRemove.isEmpty()) { + updateBuilder.setInnerRemove( + new DomainCommand.Update.DomainAddRemove.Builder() + .setStatusValues( + valuesToRemove.stream() + .map(StatusValue::fromXmlName) + .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.naturalOrder()))) + .build()); + } + + addEppInput( + clientId, + EppInput.create( + EppInput.Update.create(updateBuilder.build()), + EppExtensions.toolMetadata(reason, requestedByRegistrar)) + .withClTrid("RegistryTool")); } } diff --git a/core/src/main/resources/google/registry/reporting/spec11/ftl/daily_spec11_email.ftl b/core/src/main/resources/google/registry/reporting/spec11/ftl/daily_spec11_email.ftl new file mode 100644 index 000000000..6a1b7155b --- /dev/null +++ b/core/src/main/resources/google/registry/reporting/spec11/ftl/daily_spec11_email.ftl @@ -0,0 +1,47 @@ +<#ftl output_format="HTML"> +<#-- Copyright 2026 The Nomulus Authors. All Rights Reserved. --> + +

Dear registrar partner,

+ +

${registry} conducts a daily analysis of all domains registered in its TLDs to + identify potential security concerns. On ${date}, the following domains that your + registrar manages were flagged for potential security concerns:

+ + + + + + + <#list threats as threat> + + + + + +
Domain NameThreat Type
${threat.domainName}${threat.threatType}
+ +

Please communicate these findings to the registrant and work with the + registrant to mitigate any security issues and have the domains delisted.

+ +<#if (resources?size > 0)> +

Some helpful resources for getting off a blocked list include:

+
    + <#list resources as resource> +
  • ${resource}
  • + +
+ + +

If you believe that any of the domains were reported in error, or are still receiving + reports for issues that have been remediated, + please submit + a request to have the site reviewed.

+ +

You will continue to receive daily notices when new domains managed by your registrar + are flagged for abuse, as well as a monthly summary of all of your domains under management + that remain flagged for abuse.

+ +

If you would like to change the email to which these notices are sent, please update your + abuse contact using your registrar portal account.

+ +

If you have any questions regarding this notice, please contact ${replyToEmail}.

diff --git a/core/src/main/resources/google/registry/reporting/spec11/ftl/monthly_spec11_email.ftl b/core/src/main/resources/google/registry/reporting/spec11/ftl/monthly_spec11_email.ftl new file mode 100644 index 000000000..60ce40045 --- /dev/null +++ b/core/src/main/resources/google/registry/reporting/spec11/ftl/monthly_spec11_email.ftl @@ -0,0 +1,46 @@ +<#ftl output_format="HTML"> +<#-- Copyright 2026 The Nomulus Authors. All Rights Reserved. --> + +

Dear registrar partner,

+ +

${registry} previously notified you when the following domains managed by your + registrar were flagged for potential security concerns.

+ +

The following domains that you manage continue to be flagged by our analysis for + potential security concerns. This may be because the registrants have not completed the + requisite steps to mitigate the potential security abuse and/or have it reviewed and + delisted.

+ + + + + + + <#list threats as threat> + + + + + +
Domain NameThreat Type
${threat.domainName}${threat.threatType}
+ +

Please work with the registrant to mitigate any security issues and have the + domains delisted. If you believe that any of the domains were reported in error, or are + still receiving reports for issues that have been remediated, + please submit a + request to have the site reviewed.

+ +<#if (resources?size > 0)> +

Some helpful resources for getting off a blocked list include:

+
    + <#list resources as resource> +
  • ${resource}
  • + +
+ + +

You will continue to receive a monthly summary of all domains managed by your registrar + that remain on our lists of potential security threats. You will also receive a daily + notice when any new domains are added to these lists.

+ +

If you have any questions regarding this notice, please contact ${replyToEmail}.

diff --git a/core/src/main/resources/google/registry/reporting/spec11/soy/Spec11Email.soy b/core/src/main/resources/google/registry/reporting/spec11/soy/Spec11Email.soy deleted file mode 100644 index 0ea8a0c50..000000000 --- a/core/src/main/resources/google/registry/reporting/spec11/soy/Spec11Email.soy +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2019 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. - -{namespace registry.soy.reporting.spec11} - -/** - * Template for the content of the monthly spec11 email - */ -{template monthlySpec11Email} - {@param threats: list>} - {@param resources: list} - {@param registry: string} - {@param replyToEmail: string} - - Dear registrar partner, - -

{$registry} previously notified you when the following domains managed by your - registrar were flagged for potential security concerns.

- -

The following domains that you manage continue to be flagged by our analysis for potential - security concerns. This may be because the registrants have not completed the requisite steps - to mitigate the potential security abuse and/or have it reviewed and delisted.

- - {call threatMatchTable} - {param threats: $threats /} - {/call} - -

Please work with the registrant to mitigate any security issues and have the - domains delisted. If you believe that any of the domains were reported in error, or are still - receiving reports for issues that have been remediated, - please submit a - request to have the site reviewed.

- - {call resourceList} - {param resources: $resources /} - {/call} - -

You will continue to receive a monthly summary of all domains managed by your registrar - that remain on our lists of potential security threats. You will also receive a daily - notice when any new domains are added to these lists.

- -

If you have any questions regarding this notice, please contact {$replyToEmail}.

-{/template} - -/** - * Template for the content of the daily spec11 email - */ -{template dailySpec11Email} - {@param threats: list>} - {@param resources: list} - {@param date: string} - {@param registry: string} - {@param replyToEmail: string} - - Dear registrar partner, - -

{$registry} conducts a daily analysis of all domains registered in its TLDs to - identify potential security concerns. On {$date}, the following domains that your - registrar manages were flagged for potential security concerns:

- - {call threatMatchTable} - {param threats: $threats /} - {/call} - -

Please communicate these findings to the registrant and work with the - registrant to mitigate any security issues and have the domains delisted.

- - {call resourceList} - {param resources: $resources /} - {/call} - -

If you believe that any of the domains were reported in error, or are still receiving - reports for issues that have been remediated, - please submit - a request to have the site reviewed.

- -

You will continue to receive daily notices when new domains managed by your registrar - are flagged for abuse, as well as a monthly summary of all of your domains under management - that remain flagged for abuse.

- -

If you would like to change the email to which these notices are sent please update your - abuse contact using your registrar portal account.

- -

If you have any questions regarding this notice, please contact {$replyToEmail}.

-{/template} - -/** - * Template for the list of potentially-useful resources - */ -{template resourceList} - {@param resources: list} - {if length($resources) > 0} - Some helpful resources for getting off a blocked list include: -
    - {for $resource in $resources} -
  • {$resource}
  • - {/for} -
- {/if} -{/template} - -/** - * Template for the table containing the threats themselves - */ -{template threatMatchTable} - {@param threats: list>} - - - - - - {for $threat in $threats} - - - - - {/for} -
Domain NameThreat Type
{$threat.get('domainName')}{$threat.get('threatType')}
-{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/CreateAnchorTenant.soy b/core/src/main/resources/google/registry/tools/soy/CreateAnchorTenant.soy deleted file mode 100644 index 7078ad5a5..000000000 --- a/core/src/main/resources/google/registry/tools/soy/CreateAnchorTenant.soy +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2017 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. - -{namespace domain.registry.tools.create_anchor_tenant} - -/** - * Create anchor tenant domain - */ -{template createanchortenant stricthtml="false"} -{@param domainName: string} -{@param contactId: string} -{@param password: string} -{@param period: int} -{@param? reason: string|null} -{@param? feeCurrency: string|null} -{@param? fee: string|null} - - - - - - {$domainName} - {$period} - {$contactId} - {$contactId} - {$contactId} - - {$password} - - - - - - {if $reason} - {$reason} - {/if} - false - true - - {if $fee} - - {$feeCurrency} - {$fee} - - {/if} - - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/DomainCheck.soy b/core/src/main/resources/google/registry/tools/soy/DomainCheck.soy deleted file mode 100644 index bd869a243..000000000 --- a/core/src/main/resources/google/registry/tools/soy/DomainCheck.soy +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2017 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. - -{namespace domain.registry.tools.domain_check} - -/** - * Domain check request - */ -{template domaincheck stricthtml="false"} -{@param domainNames: list} -{@param? allocationToken: string|null} - - - - - - {for $d in $domainNames} - {$d} - {/for} - - - - - {for $d in $domainNames} - - {$d} - create - 1 - - {/for} - - {if $allocationToken} - - {$allocationToken} - - {/if} - - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/DomainCheckClaims.soy b/core/src/main/resources/google/registry/tools/soy/DomainCheckClaims.soy deleted file mode 100644 index cec71abed..000000000 --- a/core/src/main/resources/google/registry/tools/soy/DomainCheckClaims.soy +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2017 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. - -{namespace domain.registry.tools.domain_check_claims} - -/** - * Domain check claims request - */ -{template domaincheckclaims stricthtml="false"} -{@param domainNames: list} - - - - - - {for $d in $domainNames} - {$d} - {/for} - - - - - claims - - - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/DomainCreate.soy b/core/src/main/resources/google/registry/tools/soy/DomainCreate.soy deleted file mode 100644 index 9b20b85d6..000000000 --- a/core/src/main/resources/google/registry/tools/soy/DomainCreate.soy +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2017 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. - -{namespace domain.registry.tools.domain_create} -/** - * Create domain - */ -{template domaincreate stricthtml="false"} - {@param domain: string} - {@param period: int} - {@param nameservers: list} - {@param? registrant: string|null} - {@param? admins: list|null} - {@param? techs: list|null} - {@param password: string} - {@param? currency: string|null} - {@param? price: string|null} - {@param dsRecords: list<[keyTag:int, alg:int, digestType:int, digest:string]>} - {@param? reason: string|null} - {@param? requestedByRegistrar: string|null} - {@param? allocationToken: string|null} - - - - - - - {$domain} - {$period} - {if length($nameservers) > 0} - - {for $s in $nameservers} - {$s} - {/for} - - {/if} - {if $registrant != null} - {$registrant} - {/if} - {if $admins != null} - {for $admin in $admins} - {$admin} - {/for} - {/if} - {if $techs != null} - {for $tech in $techs} - {$tech} - {/for} - {/if} - - {$password} - - - - {if length($dsRecords) > 0 || $price != null || $reason || $requestedByRegistrar || $allocationToken} - - {if $price != null} - - {$currency} - {$price} - - {/if} - {if length($dsRecords) > 0} - - {for $dsRecord in $dsRecords} - - {$dsRecord.keyTag} - {$dsRecord.alg} - {$dsRecord.digestType} - {$dsRecord.digest} - - {/for} - - {/if} - {if $reason || $requestedByRegistrar} - - {if $reason} - {$reason} - {/if} - {if $requestedByRegistrar} - {$requestedByRegistrar} - {/if} - - {/if} - {if $allocationToken} - - {$allocationToken} - - {/if} - - {/if} - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/DomainDelete.soy b/core/src/main/resources/google/registry/tools/soy/DomainDelete.soy deleted file mode 100644 index 89d91474b..000000000 --- a/core/src/main/resources/google/registry/tools/soy/DomainDelete.soy +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2017 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. - -{namespace domain.registry.tools.domain_delete} - -/** - * Delete domain request - */ -{template deletedomain stricthtml="false"} -{@param domainName: string} -{@param immediately: bool} -{@param reason: string} -{@param requestedByRegistrar: any} - - - - - - {$domainName} - - - - - Deleted by registry administrator: {$reason} - {$requestedByRegistrar} - - {if $immediately} - - 0 - 0 - - {/if} - - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/DomainRenew.soy b/core/src/main/resources/google/registry/tools/soy/DomainRenew.soy deleted file mode 100644 index bf5fef9dc..000000000 --- a/core/src/main/resources/google/registry/tools/soy/DomainRenew.soy +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2018 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. - -{namespace domain.registry.tools.domain_renew} - -/** - * Renew domain request - */ -{template renewdomain stricthtml="false"} -{@param domainName: string} -{@param expirationDate: string} -{@param period: string} -{@param? reason: string|null} -{@param? requestedByRegistrar: string|null} - - - - - - {$domainName} - {$expirationDate} - {$period} - - - {if $reason || $requestedByRegistrar} - - - {if $reason} - {$reason} - {/if} - {if $requestedByRegistrar} - {$requestedByRegistrar} - {/if} - - - {/if} - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/DomainUpdate.soy b/core/src/main/resources/google/registry/tools/soy/DomainUpdate.soy deleted file mode 100644 index 7436c95d9..000000000 --- a/core/src/main/resources/google/registry/tools/soy/DomainUpdate.soy +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2017 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. - -{namespace domain.registry.tools.domain_update} -/** - * Update domain - */ -{template domainupdate stricthtml="false"} - {@param domain: string} - {@param add: bool} - {@param addNameservers: list} - {@param addStatuses: list} - {@param remove: bool} - {@param removeNameservers: list} - {@param removeStatuses: list} - {@param change: bool} - {@param? password: string|null} - {@param secdns: bool} - {@param addDsRecords: list<[keyTag:int, alg:int, digestType:int, digest:string]>} - {@param removeDsRecords: list<[keyTag:int, alg:int, digestType:int, digest:string]>} - {@param removeAllDsRecords: bool} - {@param? autorenews: string|null} - {@param? reason: string|null} - {@param? requestedByRegistrar: string|null} - - - - - - - {$domain} - {if $add} - - {if length($addNameservers) > 0} - - {for $s in $addNameservers} - {$s} - {/for} - - {/if} - {for $status in $addStatuses} - - {/for} - - {/if} - {if $remove} - - {if length($removeNameservers) > 0} - - {for $s in $removeNameservers} - {$s} - {/for} - - {/if} - {for $status in $removeStatuses} - - {/for} - - {/if} - {if $change} - - {if $password} - - {$password} - - {/if} - - {/if} - - - {if $secdns || $autorenews || $reason || $requestedByRegistrar} - - {if $secdns} - - {if $removeAllDsRecords} - - true - - {/if} - {if length($removeDsRecords) > 0} - - {for $dsRecord in $removeDsRecords} - - {$dsRecord.keyTag} - {$dsRecord.alg} - {$dsRecord.digestType} - {$dsRecord.digest} - - {/for} - - {/if} - {if length($addDsRecords) > 0} - - {for $dsRecord in $addDsRecords} - - {$dsRecord.keyTag} - {$dsRecord.alg} - {$dsRecord.digestType} - {$dsRecord.digest} - - {/for} - - {/if} - - {/if} - {if $autorenews} - - {$autorenews} - - {/if} - {if $reason || $requestedByRegistrar} - - {if $reason} - {$reason} - {/if} - {if $requestedByRegistrar} - {$requestedByRegistrar} - {/if} - - {/if} - - {/if} - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/HostCreate.soy b/core/src/main/resources/google/registry/tools/soy/HostCreate.soy deleted file mode 100644 index 0e583f916..000000000 --- a/core/src/main/resources/google/registry/tools/soy/HostCreate.soy +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2017 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. - -{namespace domain.registry.tools.host_create} -/** - * Create host - */ -{template hostcreate stricthtml="false"} - {@param hostname: string} - {@param? ipv4addresses: list|null} - {@param? ipv6addresses: list|null} - - - - - - - {$hostname} - {if $ipv4addresses} - {for $ipv4 in $ipv4addresses} - {$ipv4} - {/for} - {/if} - {if $ipv6addresses} - {for $ipv6 in $ipv6addresses} - {$ipv6} - {/for} - {/if} - - - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/HostDelete.soy b/core/src/main/resources/google/registry/tools/soy/HostDelete.soy deleted file mode 100644 index 63a07c88f..000000000 --- a/core/src/main/resources/google/registry/tools/soy/HostDelete.soy +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2017 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. - -{namespace domain.registry.tools.host_delete} - -/** - * Delete host request - */ -{template deletehost stricthtml="false"} -{@param hostName: string} -{@param reason: string} -{@param requestedByRegistrar: any} - - - - - - {$hostName} - - - - - Deleted by registry administrator: {$reason} - {$requestedByRegistrar} - - - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/RemoveIpAddress.soy b/core/src/main/resources/google/registry/tools/soy/RemoveIpAddress.soy deleted file mode 100644 index 281e0abbb..000000000 --- a/core/src/main/resources/google/registry/tools/soy/RemoveIpAddress.soy +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2017 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. - -{namespace domain.registry.tools.remove_ip_address} - -/** - * Request to remove IP addresses. - */ -{template remove_ip_address stricthtml="false"} -{@param name: string} -{@param ipAddresses: list>} -{@param requestedByRegistrar: string} - - - - - - {$name} - {for $ip in $ipAddresses} - - {$ip['address']} - - {/for} - - - - - External IP address removed by registry administrator. - {$requestedByRegistrar} - - - ABC-12345 - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/UniformRapidSuspension.soy b/core/src/main/resources/google/registry/tools/soy/UniformRapidSuspension.soy deleted file mode 100644 index 8df6e1905..000000000 --- a/core/src/main/resources/google/registry/tools/soy/UniformRapidSuspension.soy +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2017 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. - -{namespace domain.registry.tools.uniform_rapid_suspension} - -/** - * Uniform Rapid Suspension - */ -{template uniformrapidsuspension stricthtml="false"} -{@param domainName: string} -{@param hostsToAdd: list} -{@param hostsToRemove: list} -{@param statusesToApply: list} -{@param statusesToRemove: list} -{@param newDsData: list<[keyTag:int, alg:int, digestType:int, digest:string]>} -{@param reason: string} -{@param autorenews: string} - - - - - - {$domainName} - - {if length($hostsToAdd) > 0} - - {for $ha in $hostsToAdd} - {$ha} - {/for} - - {/if} - {for $la in $statusesToApply} - - {/for} - - - {if length($hostsToRemove) > 0} - - {for $hr in $hostsToRemove} - {$hr} - {/for} - - {/if} - {for $lr in $statusesToRemove} - - {/for} - - - - - - - true - - {if length($newDsData) > 0} - - {for $ds in $newDsData} - - {$ds.keyTag} - {$ds.alg} - {$ds.digestType} - {$ds.digest} - - {/for} - - {/if} - - - {$autorenews} - - - {$reason} - false - - - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/UpdateServerLocks.soy b/core/src/main/resources/google/registry/tools/soy/UpdateServerLocks.soy deleted file mode 100644 index 48706c97d..000000000 --- a/core/src/main/resources/google/registry/tools/soy/UpdateServerLocks.soy +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2017 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. - -{namespace domain.registry.tools.update_server_locks} - -/** - * Update server locks - */ -{template updateserverlocks stricthtml="false"} -{@param domainName: string} -{@param locksToApply: list} -{@param locksToRemove: list} -{@param requestedByRegistrar: any} -{@param? reason: string|null} - - - - - - {$domainName} - - {for $a in $locksToApply} - - {/for} - - - {for $r in $locksToRemove} - - {/for} - - - - - - {if $reason} - {$reason} - {/if} - {$requestedByRegistrar} - - - RegistryTool - - -{/template} diff --git a/core/src/test/java/google/registry/flows/ResourceFlowTestCase.java b/core/src/test/java/google/registry/flows/ResourceFlowTestCase.java index 8b9f04a1b..ceddfcc50 100644 --- a/core/src/test/java/google/registry/flows/ResourceFlowTestCase.java +++ b/core/src/test/java/google/registry/flows/ResourceFlowTestCase.java @@ -85,10 +85,9 @@ public abstract class ResourceFlowTestCase + + + + + example.tld + + + RegistryTool + + + """, + xml); } @Test - void testUnmarshalling_login() throws Exception { - EppInput input = unmarshal(EppInput.class, loadBytes(getClass(), "login_valid.xml").read()); - assertThat(input.getCommandWrapper().getClTrid()).hasValue("ABC-12345"); - assertThat(input.getCommandType()).isEqualTo("login"); - assertThat(input.getResourceType()).isEmpty(); - assertThat(input.getSingleTargetId()).isEmpty(); - assertThat(input.getTargetIds()).isEmpty(); - InnerCommand command = input.getCommandWrapper().getCommand(); - assertThat(command).isInstanceOf(Login.class); - Login loginCommand = (Login) command; - assertThat(loginCommand.clientId).isEqualTo("NewRegistrar"); - assertThat(loginCommand.password).isEqualTo("foo-BAR2"); - assertThat(loginCommand.newPassword).isNull(); - assertThat(loginCommand.options.version).isEqualTo("1.0"); - assertThat(loginCommand.options.language).isEqualTo("en"); - assertThat(loginCommand.services.objectServices) - .containsExactly("urn:ietf:params:xml:ns:host-1.0", "urn:ietf:params:xml:ns:domain-1.0"); - assertThat(loginCommand.services.serviceExtensions) - .containsExactly("urn:ietf:params:xml:ns:launch-1.0", "urn:ietf:params:xml:ns:rgp-1.0"); + void testBuilder_nullExtensions_omitsExtensionTag() throws Exception { + EppInput eppInput = + new EppInput.Builder() + .setCommandWrapper( + new EppInput.CommandWrapper.Builder() + .setCommand( + new EppInput.Create.Builder() + .setResourceCommand( + new DomainCommand.Create.Builder() + .setDomainName("example.tld") + .build()) + .build()) + .setExtensions(null) + .setClTrid("RegistryTool") + .build()) + .build(); + + String xml = + new String(EppXmlTransformer.marshalInput(eppInput, ValidationMode.LENIENT), UTF_8); + assertXmlEquals( + """ + + + + + + example.tld + + + RegistryTool + + + """, + xml); } @Test - void testUnmarshalling_loginTagInWrongCase_throws() { - assertThrows( - XmlException.class, - () -> unmarshal(EppInput.class, loadBytes(getClass(), "login_wrong_case.xml").read())); + void testBuilder_domainUpdate_emptyAddRemove_omitsInnerTags() throws Exception { + EppInput eppInput = + new EppInput.Builder() + .setCommandWrapper( + new EppInput.CommandWrapper.Builder() + .setCommand( + new EppInput.Update.Builder() + .setResourceCommand( + new DomainCommand.Update.Builder() + .setTargetId("example.tld") + .setInnerAdd( + new DomainCommand.Update.DomainAddRemove.Builder().build()) + .setInnerRemove( + new DomainCommand.Update.DomainAddRemove.Builder().build()) + .build()) + .build()) + .setClTrid("RegistryTool") + .build()) + .build(); + + String xml = + new String(EppXmlTransformer.marshalInput(eppInput, ValidationMode.LENIENT), UTF_8); + assertXmlEquals( + """ + + + + + + example.tld + + + + + RegistryTool + + + """, + xml); + } + + @Test + void testBuilder_domainUpdate_withStatuses() throws Exception { + EppInput eppInput = + new EppInput.Builder() + .setCommandWrapper( + new EppInput.CommandWrapper.Builder() + .setCommand( + new EppInput.Update.Builder() + .setResourceCommand( + new DomainCommand.Update.Builder() + .setTargetId("example.tld") + .setInnerAdd( + new DomainCommand.Update.DomainAddRemove.Builder() + .setStatusValues( + ImmutableSet.of(StatusValue.CLIENT_HOLD)) + .build()) + .build()) + .build()) + .setClTrid("RegistryTool") + .build()) + .build(); + + String xml = + new String(EppXmlTransformer.marshalInput(eppInput, ValidationMode.LENIENT), UTF_8); + assertXmlEquals( + """ + + + + + + example.tld + + + + + + RegistryTool + + + """, + xml); } } diff --git a/core/src/test/java/google/registry/reporting/spec11/PublishSpec11ReportActionTest.java b/core/src/test/java/google/registry/reporting/spec11/PublishSpec11ReportActionTest.java index 25079f2d3..4cf4aba72 100644 --- a/core/src/test/java/google/registry/reporting/spec11/PublishSpec11ReportActionTest.java +++ b/core/src/test/java/google/registry/reporting/spec11/PublishSpec11ReportActionTest.java @@ -37,7 +37,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.net.MediaType; import google.registry.beam.spec11.ThreatMatch; -import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo; +import google.registry.reporting.spec11.Spec11EmailUtils.Spec11EmailTemplate; import google.registry.testing.FakeResponse; import java.io.IOException; import java.time.LocalDate; @@ -76,7 +76,6 @@ class PublishSpec11ReportActionTest { expectedJob = new Job(); when(get.execute()).thenReturn(expectedJob); emailUtils = mock(Spec11EmailUtils.class); - parser = mock(Spec11RegistrarThreatMatchesParser.class); response = new FakeResponse(); parser = mock(Spec11RegistrarThreatMatchesParser.class); publishAction = @@ -113,7 +112,7 @@ class PublishSpec11ReportActionTest { verify(emailUtils) .emailSpec11Reports( secondOfMonth, - Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, + Spec11EmailTemplate.MONTHLY, "Super Cool Registry Monthly Threat Detector [2018-06-02]", sampleThreatMatches()); verifyNoMoreInteractions(emailUtils); @@ -163,7 +162,7 @@ class PublishSpec11ReportActionTest { verify(emailUtils) .emailSpec11Reports( date, - Spec11EmailSoyInfo.DAILY_SPEC_11_EMAIL, + Spec11EmailTemplate.DAILY, "Super Cool Registry Daily Threat Detector [2018-06-05]", sampleThreatMatches()); verifyNoMoreInteractions(emailUtils); @@ -196,7 +195,7 @@ class PublishSpec11ReportActionTest { verify(emailUtils) .emailSpec11Reports( date, - Spec11EmailSoyInfo.DAILY_SPEC_11_EMAIL, + Spec11EmailTemplate.DAILY, "Super Cool Registry Daily Threat Detector [2018-06-05]", expectedMatchSet); verifyNoMoreInteractions(emailUtils); @@ -214,7 +213,7 @@ class PublishSpec11ReportActionTest { verify(emailUtils) .emailSpec11Reports( date, - Spec11EmailSoyInfo.DAILY_SPEC_11_EMAIL, + Spec11EmailTemplate.DAILY, "Super Cool Registry Daily Threat Detector [2018-06-05]", ImmutableSet.of()); verifyNoMoreInteractions(emailUtils); diff --git a/core/src/test/java/google/registry/reporting/spec11/Spec11EmailUtilsTest.java b/core/src/test/java/google/registry/reporting/spec11/Spec11EmailUtilsTest.java index 2085bdfb3..e2ec72794 100644 --- a/core/src/test/java/google/registry/reporting/spec11/Spec11EmailUtilsTest.java +++ b/core/src/test/java/google/registry/reporting/spec11/Spec11EmailUtilsTest.java @@ -23,6 +23,7 @@ import static google.registry.reporting.spec11.Spec11RegistrarThreatMatchesParse import static google.registry.reporting.spec11.Spec11RegistrarThreatMatchesParserTest.sampleThreatMatches; import static google.registry.testing.DatabaseHelper.createTld; import static google.registry.testing.DatabaseHelper.loadByEntity; +import static google.registry.testing.DatabaseHelper.newDomain; import static google.registry.testing.DatabaseHelper.persistActiveHost; import static google.registry.testing.DatabaseHelper.persistResource; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -39,10 +40,10 @@ import google.registry.model.domain.Domain; import google.registry.model.host.Host; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; -import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo; -import google.registry.testing.DatabaseHelper; +import google.registry.reporting.spec11.Spec11EmailUtils.Spec11EmailTemplate; import google.registry.util.EmailMessage; import google.registry.util.Sleeper; +import google.registry.util.TemplateRenderer; import jakarta.mail.MessagingException; import jakarta.mail.internet.InternetAddress; import java.time.Duration; @@ -64,40 +65,80 @@ class Spec11EmailUtilsTest { private static final ImmutableList FAKE_RESOURCES = ImmutableList.of("foo"); private static final String DAILY_EMAIL_FORMAT = - "Dear registrar partner,

Super Cool Registry conducts a daily analysis of all domains" - + " registered in its TLDs to identify potential security concerns. On 2018-07-15, the" - + " following domains that your registrar manages were flagged for potential security" - + " concerns:

%s" - + "
Domain NameThreat Type

Please communicate these findings to the registrant and work with the" - + " registrant to mitigate any security issues and have the domains delisted.

" - + "Some helpful resources for getting off a blocked list include:" - + "
  • foo

If you believe that any of the domains were reported in" - + " error, or are still receiving reports for issues that have been remediated, please" - + " submit" - + " a request to have the site reviewed.

You will continue to receive daily" - + " notices when new domains managed by your registrar are flagged for abuse, as well as" - + " a monthly summary of all of your domains under management that remain flagged for" - + " abuse.

If you would like to change the email to which these notices are sent" - + " please update your abuse contact using your registrar portal account.

If you" - + " have any questions regarding this notice, please contact abuse@test.com.

"; + """ + +

Dear registrar partner,

+ +

Super Cool Registry conducts a daily analysis of all domains registered in its TLDs to + identify potential security concerns. On 2018-07-15, the following domains that your + registrar manages were flagged for potential security concerns:

+ + + + + + + %s
Domain NameThreat Type
+ +

Please communicate these findings to the registrant and work with the + registrant to mitigate any security issues and have the domains delisted.

+ +

Some helpful resources for getting off a blocked list include:

+
    +
  • foo
  • +
+ +

If you believe that any of the domains were reported in error, or are still receiving + reports for issues that have been remediated, + please submit + a request to have the site reviewed.

+ +

You will continue to receive daily notices when new domains managed by your registrar + are flagged for abuse, as well as a monthly summary of all of your domains under management + that remain flagged for abuse.

+ +

If you would like to change the email to which these notices are sent, please update your + abuse contact using your registrar portal account.

+ +

If you have any questions regarding this notice, please contact abuse@test.com.

+ """; private static final String MONTHLY_EMAIL_FORMAT = - "Dear registrar partner,

Super Cool Registry previously notified you when the following" - + " domains managed by your registrar were flagged for potential security" - + " concerns.

The following domains that you manage continue to be flagged by our" - + " analysis for potential security concerns. This may be because the registrants have" - + " not completed the requisite steps to mitigate the potential security abuse and/or" - + " have it reviewed and delisted.

%s
Domain NameThreat" - + " Type

Please work with the registrant to mitigate any security" - + " issues and have the domains delisted. If you believe that any of the domains were" - + " reported in error, or are still receiving reports for issues that have been" - + " remediated, please submit a" - + " request to have the site reviewed.

Some helpful resources for getting off a" - + " blocked list include:
  • foo

You will continue to receive a monthly" - + " summary of all domains managed by your registrar that remain on our lists of" - + " potential security threats. You will also receive a daily notice when any new" - + " domains are added to these lists.

If you have any questions regarding this" - + " notice, please contact abuse@test.com.

"; + """ + +

Dear registrar partner,

+ +

Super Cool Registry previously notified you when the following domains managed by your + registrar were flagged for potential security concerns.

+ +

The following domains that you manage continue to be flagged by our analysis for + potential security concerns. This may be because the registrants have not completed the + requisite steps to mitigate the potential security abuse and/or have it reviewed and + delisted.

+ + + + + + + %s
Domain NameThreat Type
+ +

Please work with the registrant to mitigate any security issues and have the + domains delisted. If you believe that any of the domains were reported in error, or are + still receiving reports for issues that have been remediated, + please submit a + request to have the site reviewed.

+ +

Some helpful resources for getting off a blocked list include:

+
    +
  • foo
  • +
+ +

You will continue to receive a monthly summary of all domains managed by your registrar + that remain on our lists of potential security threats. You will also receive a daily + notice when any new domains are added to these lists.

+ +

If you have any questions regarding this notice, please contact abuse@test.com.

+ """; @RegisterExtension final JpaIntegrationTestExtension jpa = @@ -120,6 +161,7 @@ class Spec11EmailUtilsTest { new Spec11EmailUtils( gmailClient, sleeper, + new TemplateRenderer(), emailThrottleDuration, new InternetAddress("my-receiver@test.com"), new InternetAddress("abuse@test.com"), @@ -139,7 +181,7 @@ class Spec11EmailUtilsTest { void testSuccess_sleepsBetweenSending() throws Exception { emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, + Spec11EmailTemplate.MONTHLY, "Super Cool Registry Monthly Threat Detector [2018-07-15]", sampleThreatMatches()); // We inspect individual parameters because Message doesn't implement equals(). @@ -152,7 +194,7 @@ class Spec11EmailUtilsTest { void testSuccess_emailMonthlySpec11Reports() throws Exception { emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, + Spec11EmailTemplate.MONTHLY, "Super Cool Registry Monthly Threat Detector [2018-07-15]", sampleThreatMatches()); // We inspect individual parameters because Message doesn't implement equals(). @@ -163,7 +205,14 @@ class Spec11EmailUtilsTest { "the.registrar@example.com", ImmutableList.of("abuse@test.com", "bcc@test.com"), "Super Cool Registry Monthly Threat Detector [2018-07-15]", - String.format(MONTHLY_EMAIL_FORMAT, "a[.]comMALWARE"), + String.format( + MONTHLY_EMAIL_FORMAT, + """ + + a[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedContents.get(1), @@ -172,7 +221,16 @@ class Spec11EmailUtilsTest { "Super Cool Registry Monthly Threat Detector [2018-07-15]", String.format( MONTHLY_EMAIL_FORMAT, - "b[.]comMALWAREc[.]comMALWARE"), + """ + + b[.]com + MALWARE + + + c[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedContents.get(2), @@ -187,7 +245,7 @@ class Spec11EmailUtilsTest { void testSuccess_emailDailySpec11Reports() throws Exception { emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.DAILY_SPEC_11_EMAIL, + Spec11EmailTemplate.DAILY, "Super Cool Registry Daily Threat Detector [2018-07-15]", sampleThreatMatches()); // We inspect individual parameters because Message doesn't implement equals(). @@ -198,7 +256,14 @@ class Spec11EmailUtilsTest { "the.registrar@example.com", ImmutableList.of("abuse@test.com", "bcc@test.com"), "Super Cool Registry Daily Threat Detector [2018-07-15]", - String.format(DAILY_EMAIL_FORMAT, "a[.]comMALWARE"), + String.format( + DAILY_EMAIL_FORMAT, + """ + + a[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedMessages.get(1), @@ -207,7 +272,16 @@ class Spec11EmailUtilsTest { "Super Cool Registry Daily Threat Detector [2018-07-15]", String.format( DAILY_EMAIL_FORMAT, - "b[.]comMALWAREc[.]comMALWARE"), + """ + + b[.]com + MALWARE + + + c[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedMessages.get(2), @@ -225,7 +299,7 @@ class Spec11EmailUtilsTest { persistResource(loadByEntity(bDomain).asBuilder().addStatusValue(CLIENT_HOLD).build()); emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, + Spec11EmailTemplate.MONTHLY, "Super Cool Registry Monthly Threat Detector [2018-07-15]", sampleThreatMatches()); // We inspect individual parameters because Message doesn't implement equals(). @@ -236,7 +310,14 @@ class Spec11EmailUtilsTest { "new.registrar@example.com", ImmutableList.of("abuse@test.com", "bcc@test.com"), "Super Cool Registry Monthly Threat Detector [2018-07-15]", - String.format(MONTHLY_EMAIL_FORMAT, "c[.]comMALWARE"), + String.format( + MONTHLY_EMAIL_FORMAT, + """ + + c[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedContents.get(1), @@ -252,11 +333,13 @@ class Spec11EmailUtilsTest { // Create an inactive domain and an active domain with the same name. persistResource(loadByEntity(aDomain).asBuilder().addStatusValue(SERVER_HOLD).build()); Host host = persistActiveHost("ns1.example.com"); - aDomain = persistResource(aDomain.asBuilder().setNameservers(host.createVKey()).build()); + aDomain = + persistResource( + aDomain.asBuilder().setNameservers(ImmutableSet.of(host.createVKey())).build()); emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, + Spec11EmailTemplate.MONTHLY, "Super Cool Registry Monthly Threat Detector [2018-07-15]", sampleThreatMatches()); // We inspect individual parameters because Message doesn't implement equals(). @@ -267,7 +350,14 @@ class Spec11EmailUtilsTest { "the.registrar@example.com", ImmutableList.of("abuse@test.com", "bcc@test.com"), "Super Cool Registry Monthly Threat Detector [2018-07-15]", - String.format(MONTHLY_EMAIL_FORMAT, "a[.]comMALWARE"), + String.format( + MONTHLY_EMAIL_FORMAT, + """ + + a[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedContents.get(1), @@ -276,7 +366,16 @@ class Spec11EmailUtilsTest { "Super Cool Registry Monthly Threat Detector [2018-07-15]", String.format( MONTHLY_EMAIL_FORMAT, - "b[.]comMALWAREc[.]comMALWARE"), + """ + + b[.]com + MALWARE + + + c[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedContents.get(2), @@ -304,7 +403,7 @@ class Spec11EmailUtilsTest { () -> emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, + Spec11EmailTemplate.MONTHLY, "Super Cool Registry Monthly Threat Detector [2018-07-15]", ImmutableSet.copyOf(matches))); assertThat(thrown) @@ -319,7 +418,14 @@ class Spec11EmailUtilsTest { "the.registrar@example.com", ImmutableList.of("abuse@test.com", "bcc@test.com"), "Super Cool Registry Monthly Threat Detector [2018-07-15]", - String.format(MONTHLY_EMAIL_FORMAT, "a[.]comMALWARE"), + String.format( + MONTHLY_EMAIL_FORMAT, + """ + + a[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedMessages.get(1), @@ -328,7 +434,16 @@ class Spec11EmailUtilsTest { "Super Cool Registry Monthly Threat Detector [2018-07-15]", String.format( MONTHLY_EMAIL_FORMAT, - "b[.]comMALWAREc[.]comMALWARE"), + """ + + b[.]com + MALWARE + + + c[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedMessages.get(2), @@ -363,7 +478,7 @@ class Spec11EmailUtilsTest { .build()); emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, + Spec11EmailTemplate.MONTHLY, "Super Cool Registry Monthly Threat Detector [2018-07-15]", sampleThreatMatches()); verify(gmailClient, times(3)).sendEmail(contentCaptor.capture()); @@ -379,7 +494,7 @@ class Spec11EmailUtilsTest { () -> emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, + Spec11EmailTemplate.MONTHLY, "Super Cool Registry Monthly Threat Detector [2018-07-15]", ImmutableSet.of( RegistrarThreatMatches.create( @@ -409,12 +524,13 @@ class Spec11EmailUtilsTest { expectedContentBuilder.addBcc(new InternetAddress(bcc)); } contentType.ifPresent(expectedContentBuilder::setContentType); + assertThat(message.body()).isEqualTo(body); assertThat(message).isEqualTo(expectedContentBuilder.build()); } private static Domain persistDomainWithHost(String domainName, Host host) { return persistResource( - DatabaseHelper.newDomain(domainName) + newDomain(domainName) .asBuilder() .setNameservers(ImmutableSet.of(host.createVKey())) .build()); diff --git a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant.xml b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant.xml index 4435e4601..6a7aecd05 100644 --- a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant.xml +++ b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant.xml @@ -1,13 +1,10 @@ + - + example.tld 2 - jd1234 - jd1234 - jd1234 abcdefghijklmnop diff --git a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_fee_premium.xml b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_fee_premium.xml index a8baf0c38..10944ac8c 100644 --- a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_fee_premium.xml +++ b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_fee_premium.xml @@ -1,13 +1,10 @@ + - + premium.tld 2 - jd1234 - jd1234 - jd1234 abcdefghijklmnop diff --git a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_fee_standard.xml b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_fee_standard.xml index 08030ce7f..ab703522b 100644 --- a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_fee_standard.xml +++ b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_fee_standard.xml @@ -1,13 +1,10 @@ + - + example.tld 2 - jd1234 - jd1234 - jd1234 abcdefghijklmnop diff --git a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_multiple_word_reason.xml b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_multiple_word_reason.xml index 3479f42a4..59a285b1f 100644 --- a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_multiple_word_reason.xml +++ b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_multiple_word_reason.xml @@ -1,13 +1,10 @@ + - + example.tld 2 - jd1234 - jd1234 - jd1234 abcdefghijklmnop diff --git a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_no_reason.xml b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_no_reason.xml index 8e460d390..5a9967d0b 100644 --- a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_no_reason.xml +++ b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_no_reason.xml @@ -1,13 +1,10 @@ + - + example.tld 2 - jd1234 - jd1234 - jd1234 abcdefghijklmnop diff --git a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_password.xml b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_password.xml index 1a1472f7a..8df4654b2 100644 --- a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_password.xml +++ b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_password.xml @@ -1,13 +1,10 @@ + - + example.tld 2 - jd1234 - jd1234 - jd1234 foo diff --git a/core/src/test/resources/google/registry/tools/server/domain_create_palladium.xml b/core/src/test/resources/google/registry/tools/server/domain_create_palladium.xml index a2363d29f..5848e9edb 100644 --- a/core/src/test/resources/google/registry/tools/server/domain_create_palladium.xml +++ b/core/src/test/resources/google/registry/tools/server/domain_create_palladium.xml @@ -1,9 +1,8 @@ - + - + palladium.tld 1 diff --git a/core/src/test/resources/google/registry/tools/server/domain_create_parajiumu_3yrs.xml b/core/src/test/resources/google/registry/tools/server/domain_create_parajiumu_3yrs.xml index fc89c75bb..18f2a8f1e 100644 --- a/core/src/test/resources/google/registry/tools/server/domain_create_parajiumu_3yrs.xml +++ b/core/src/test/resources/google/registry/tools/server/domain_create_parajiumu_3yrs.xml @@ -1,9 +1,8 @@ - + - + parajiumu.baar 3 diff --git a/core/src/test/resources/google/registry/tools/server/update_server_locks_apply_all.xml b/core/src/test/resources/google/registry/tools/server/update_server_locks_apply_all.xml index da247f34b..15399b653 100644 --- a/core/src/test/resources/google/registry/tools/server/update_server_locks_apply_all.xml +++ b/core/src/test/resources/google/registry/tools/server/update_server_locks_apply_all.xml @@ -5,13 +5,12 @@ example.tld - - - - - + + + + + -
diff --git a/core/src/test/resources/google/registry/tools/server/update_server_locks_apply_one.xml b/core/src/test/resources/google/registry/tools/server/update_server_locks_apply_one.xml index 4f16abdf5..32cb7b8f3 100644 --- a/core/src/test/resources/google/registry/tools/server/update_server_locks_apply_one.xml +++ b/core/src/test/resources/google/registry/tools/server/update_server_locks_apply_one.xml @@ -5,9 +5,8 @@ example.tld - + -
diff --git a/core/src/test/resources/google/registry/tools/server/update_server_locks_multiple_word_reason.xml b/core/src/test/resources/google/registry/tools/server/update_server_locks_multiple_word_reason.xml index 7517dfd5d..8599d9c2c 100644 --- a/core/src/test/resources/google/registry/tools/server/update_server_locks_multiple_word_reason.xml +++ b/core/src/test/resources/google/registry/tools/server/update_server_locks_multiple_word_reason.xml @@ -5,9 +5,8 @@ example.tld - + - diff --git a/core/src/test/resources/google/registry/tools/server/update_server_locks_remove_all.xml b/core/src/test/resources/google/registry/tools/server/update_server_locks_remove_all.xml index 438460b1e..2266889ae 100644 --- a/core/src/test/resources/google/registry/tools/server/update_server_locks_remove_all.xml +++ b/core/src/test/resources/google/registry/tools/server/update_server_locks_remove_all.xml @@ -4,13 +4,12 @@ example.tld - - - - - - + + + + + diff --git a/core/src/test/resources/google/registry/tools/server/update_server_locks_remove_one.xml b/core/src/test/resources/google/registry/tools/server/update_server_locks_remove_one.xml index 08a80cf10..188d4a4f7 100644 --- a/core/src/test/resources/google/registry/tools/server/update_server_locks_remove_one.xml +++ b/core/src/test/resources/google/registry/tools/server/update_server_locks_remove_one.xml @@ -4,9 +4,8 @@ example.tld - - + diff --git a/core/src/test/resources/google/registry/tools/server/xn--q9jyb4c_2010-10-17_full_S1_R0.xml b/core/src/test/resources/google/registry/tools/server/xn--q9jyb4c_2010-10-17_full_S1_R0.xml index ae19d0cfb..0f9f7f82f 100644 --- a/core/src/test/resources/google/registry/tools/server/xn--q9jyb4c_2010-10-17_full_S1_R0.xml +++ b/core/src/test/resources/google/registry/tools/server/xn--q9jyb4c_2010-10-17_full_S1_R0.xml @@ -131,7 +131,7 @@ 0 1 2 - 4 + 3 diff --git a/db/buildscript-gradle.lockfile b/db/buildscript-gradle.lockfile index d109decc7..929906e70 100644 --- a/db/buildscript-gradle.lockfile +++ b/db/buildscript-gradle.lockfile @@ -4,8 +4,8 @@ com.fasterxml.jackson.core:jackson-annotations:2.21=classpath gradle.plugin.org.flywaydb:gradle-plugin-publishing:12.2.0=classpath org.flywaydb.flyway:org.flywaydb.flyway.gradle.plugin:12.2.0=classpath -org.flywaydb:flyway-core:12.6.1=classpath -org.flywaydb:flyway-database-postgresql:12.6.1=classpath +org.flywaydb:flyway-core:12.6.2=classpath +org.flywaydb:flyway-database-postgresql:12.6.2=classpath tools.jackson.core:jackson-core:3.1.1=classpath tools.jackson.core:jackson-databind:3.1.1=classpath tools.jackson:jackson-bom:3.1.1=classpath diff --git a/db/gradle.lockfile b/db/gradle.lockfile index ec6eb6cd0..d498b059e 100644 --- a/db/gradle.lockfile +++ b/db/gradle.lockfile @@ -71,7 +71,7 @@ dnsjava:dnsjava:3.6.4=deploy_jar,runtimeClasspath,testRuntimeClasspath info.picocli:picocli:4.7.7=checkstyle io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.16=testCompileClasspath,testRuntimeClasspath +io.github.java-diff-utils:java-diff-utils:4.17=testCompileClasspath,testRuntimeClasspath io.grpc:grpc-api:1.70.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-context:1.70.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.opencensus:opencensus-api:0.31.1=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -109,8 +109,9 @@ org.codehaus.plexus:plexus-classworlds:2.6.0=checkstyle org.codehaus.plexus:plexus-component-annotations:2.1.0=checkstyle org.codehaus.plexus:plexus-container-default:2.1.0=checkstyle org.codehaus.plexus:plexus-utils:3.3.0=checkstyle -org.flywaydb:flyway-core:12.6.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.flywaydb:flyway-database-postgresql:12.6.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.flywaydb:flyway-core:12.6.2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.flywaydb:flyway-database-postgresql:12.6.2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.freemarker:freemarker:2.3.34=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath org.jacoco:org.jacoco.agent:0.8.14=jacocoAgent,jacocoAnt org.jacoco:org.jacoco.ant:0.8.14=jacocoAnt diff --git a/dependencies.gradle b/dependencies.gradle index 4395e93d0..8fe8bd0e6 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -84,7 +84,7 @@ ext { 'com.google.apis:google-api-services-drive:[v3-rev20240521-2.0.0,)', 'com.google.apis:google-api-services-gmail:[v1-rev20220404-2.0.0,)', 'com.google.apis:google-api-services-groupssettings:[v1-rev82-1.25.0,)', - 'com.google.apis:google-api-services-iam:[v1-rev20240118-2.0.0,0)', + 'com.google.apis:google-api-services-iam:[v1-rev20240118-2.0.0,)', 'com.google.apis:google-api-services-monitoring:[v3-rev20240519-2.0.0,)', 'com.google.apis:google-api-services-sheets:[v4-rev20240514-2.0.0,)', 'com.google.apis:google-api-services-storage:[v1-rev20210127-1.31.0,)', @@ -123,9 +123,9 @@ ext { 'com.google.oauth-client:google-oauth-client-servlet:[1.31.4,)', 'com.google.oauth-client:google-oauth-client:[1.31.4,)', 'com.google.re2j:re2j:[1.6,)', - 'com.google.template:soy:[2024-02-26,)', 'com.google.truth:truth:[1.1.2,)', 'com.googlecode.json-simple:json-simple:[1.1.1,)', + 'org.freemarker:freemarker:[2.3.32,)', 'com.ibm.icu:icu4j:[68.2,)', 'com.jcraft:jsch:[0.1.55,)', 'com.squareup:javapoet:[1.13.0,)', @@ -141,7 +141,7 @@ ext { 'io.netty:netty-tcnative-boringssl-static:[2.0.36.Final,)', 'jakarta.inject:jakarta.inject-api:[2.0.0,)', 'jakarta.mail:jakarta.mail-api:[2.1.3,)', - 'jakarta.persistence:jakarta.persistence-api:[3.2.0,4.+)', + 'jakarta.persistence:jakarta.persistence-api:[3.2.0,4.0.0)', 'jakarta.servlet:jakarta.servlet-api:[6.0,6.1)', 'jakarta.xml.bind:jakarta.xml.bind-api:[4.0.2,)', // Antlr is not a direct dependency, but we need to ensure that the diff --git a/jetty/gradle.lockfile b/jetty/gradle.lockfile index f152ea216..5b3f4574c 100644 --- a/jetty/gradle.lockfile +++ b/jetty/gradle.lockfile @@ -1,7 +1,6 @@ # This is a Gradle generated file for dependency locking. # Manual edits can break the build and are not advised. # This file is expected to be part of source control. -aopalliance:aopalliance:1.0=deploy_jar,runtimeClasspath,testRuntimeClasspath args4j:args4j:2.33=deploy_jar,runtimeClasspath,testRuntimeClasspath com.charleskorn.kaml:kaml:0.20.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.fasterxml.jackson.core:jackson-annotations:2.21=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -70,13 +69,13 @@ com.google.api:api-common:2.57.1=deploy_jar,runtimeClasspath,testRuntimeClasspat com.google.api:gax-grpc:2.74.1=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.api:gax-httpjson:2.74.1=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.api:gax:2.74.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.apis:google-api-services-admin-directory:directory_v1-rev20260227-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.apis:google-api-services-admin-directory:directory_v1-rev20260421-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-bigquery:v2-rev20251012-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-cloudresourcemanager:v1-rev20250606-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.apis:google-api-services-dataflow:v1b3-rev20260405-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.apis:google-api-services-dataflow:v1b3-rev20260503-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-dns:v1-rev20260421-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-drive:v3-rev20260428-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.apis:google-api-services-gmail:v1-rev20260427-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.apis:google-api-services-gmail:v1-rev20260511-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-groupssettings:v1-rev20220614-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-healthcare:v1-rev20240130-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-iam:v2-rev20250502-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -122,7 +121,6 @@ com.google.cloud:libraries-bom:26.48.0=deploy_jar,runtimeClasspath,testRuntimeCl com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.37.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.code.findbugs:jsr305:3.0.2=checkstyle,deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.code.gson:gson:2.13.2=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.common.html.types:types:1.0.8=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.dagger:dagger:2.59.2=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.errorprone:error_prone_annotation:2.48.0=annotationProcessor,testAnnotationProcessor com.google.errorprone:error_prone_annotations:2.36.0=checkstyle @@ -130,11 +128,10 @@ com.google.errorprone:error_prone_annotations:2.48.0=annotationProcessor,testAnn com.google.errorprone:error_prone_annotations:2.49.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.errorprone:error_prone_check_api:2.48.0=annotationProcessor,testAnnotationProcessor com.google.errorprone:error_prone_core:2.48.0=annotationProcessor,testAnnotationProcessor -com.google.escapevelocity:escapevelocity:1.1=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.flatbuffers:flatbuffers-java:24.3.25=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.flogger:flogger-system-backend:0.7.4=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.flogger:flogger:0.7.4=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.flogger:google-extensions:0.7.4=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.flogger:google-extensions:0.7.1=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.googlejavaformat:google-java-format:1.34.1=annotationProcessor,testAnnotationProcessor com.google.guava:failureaccess:1.0.3=annotationProcessor,checkstyle,deploy_jar,runtimeClasspath,testAnnotationProcessor,testRuntimeClasspath com.google.guava:guava:33.4.8-jre=checkstyle @@ -148,7 +145,6 @@ com.google.http-client:google-http-client-gson:2.1.0=deploy_jar,runtimeClasspath com.google.http-client:google-http-client-jackson2:1.46.3=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.http-client:google-http-client-protobuf:2.1.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.http-client:google-http-client:2.1.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.inject:guice:7.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.j2objc:j2objc-annotations:3.0.0=checkstyle com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,deploy_jar,runtimeClasspath,testAnnotationProcessor,testRuntimeClasspath com.google.jsinterop:jsinterop-annotations:2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -160,9 +156,8 @@ com.google.oauth-client:google-oauth-client-servlet:1.36.0=deploy_jar,runtimeCla com.google.oauth-client:google-oauth-client:1.39.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.protobuf:protobuf-java-util:4.33.2=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,testAnnotationProcessor -com.google.protobuf:protobuf-java:4.35.0-RC2=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java:4.35.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.re2j:re2j:1.8=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.template:soy:2024-02-26=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.truth:truth:1.4.5=deploy_jar,runtimeClasspath,testRuntimeClasspath com.googlecode.json-simple:json-simple:1.1.1=deploy_jar,runtimeClasspath,testRuntimeClasspath com.ibm.icu:icu4j:73.2=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -202,7 +197,7 @@ io.apicurio:apicurio-registry-protobuf-schema-utilities:3.0.0.M2=deploy_jar,runt io.github.classgraph:classgraph:4.8.162=deploy_jar,runtimeClasspath,testRuntimeClasspath io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.16=deploy_jar,runtimeClasspath,testRuntimeClasspath +io.github.java-diff-utils:java-diff-utils:4.17=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-alts:1.76.2=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-api:1.76.2=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-auth:1.76.2=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -275,7 +270,6 @@ jakarta.servlet:jakarta.servlet-api:6.0.0=deploy_jar,runtimeClasspath,testRuntim jakarta.transaction:jakarta.transaction-api:2.0.1=deploy_jar,runtimeClasspath,testRuntimeClasspath jakarta.xml.bind:jakarta.xml.bind-api:4.0.4=deploy_jar,runtimeClasspath,testRuntimeClasspath javax.annotation:javax.annotation-api:1.3.2=deploy_jar,runtimeClasspath,testRuntimeClasspath -javax.annotation:jsr250-api:1.0=deploy_jar,runtimeClasspath,testRuntimeClasspath javax.inject:javax.inject:1=annotationProcessor,deploy_jar,runtimeClasspath,testAnnotationProcessor,testRuntimeClasspath javax.jdo:jdo2-api:2.3-20090302111651=deploy_jar,runtimeClasspath,testRuntimeClasspath javax.validation:validation-api:1.0.0.GA=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -344,8 +338,9 @@ org.codehaus.plexus:plexus-utils:3.3.0=checkstyle org.conscrypt:conscrypt-openjdk-uber:2.5.2=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:angus-activation:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:jakarta.mail:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.flywaydb:flyway-core:12.6.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.flywaydb:flyway-database-postgresql:12.6.1=deploy_jar,runtimeClasspath,testRuntimeClasspath +org.flywaydb:flyway-core:12.6.2=deploy_jar,runtimeClasspath,testRuntimeClasspath +org.flywaydb:flyway-database-postgresql:12.6.2=deploy_jar,runtimeClasspath,testRuntimeClasspath +org.freemarker:freemarker:2.3.34=deploy_jar,runtimeClasspath,testRuntimeClasspath org.glassfish.jaxb:jaxb-core:4.0.6=deploy_jar,runtimeClasspath,testRuntimeClasspath org.glassfish.jaxb:jaxb-runtime:4.0.6=deploy_jar,runtimeClasspath,testRuntimeClasspath org.glassfish.jaxb:txw2:4.0.6=deploy_jar,runtimeClasspath,testRuntimeClasspath diff --git a/load-testing/gradle.lockfile b/load-testing/gradle.lockfile index ebae8ed8e..d08a84670 100644 --- a/load-testing/gradle.lockfile +++ b/load-testing/gradle.lockfile @@ -31,14 +31,14 @@ commons-collections:commons-collections:3.2.2=checkstyle info.picocli:picocli:4.7.7=checkstyle io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor -io.netty:netty-buffer:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec-http:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-common:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-handler:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-resolver:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport-native-unix-common:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-buffer:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-http:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-common:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-handler:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-resolver:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport-native-unix-common:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath javax.inject:javax.inject:1=annotationProcessor,testAnnotationProcessor net.sf.saxon:Saxon-HE:12.5=checkstyle org.antlr:antlr4-runtime:4.13.2=checkstyle diff --git a/networking/gradle.lockfile b/networking/gradle.lockfile index 11e3304de..57089825b 100644 --- a/networking/gradle.lockfile +++ b/networking/gradle.lockfile @@ -57,9 +57,9 @@ com.google.http-client:google-http-client:2.1.0=deploy_jar,runtimeClasspath,test com.google.j2objc:j2objc-annotations:3.0.0=checkstyle,testCompileClasspath com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,compileClasspath,deploy_jar,runtimeClasspath,testAnnotationProcessor,testRuntimeClasspath com.google.oauth-client:google-oauth-client:1.36.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.protobuf:protobuf-java-util:4.35.0-RC2=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java-util:4.35.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,testAnnotationProcessor -com.google.protobuf:protobuf-java:4.35.0-RC2=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java:4.35.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.re2j:re2j:1.7=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.truth:truth:1.4.5=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.ibm.icu:icu4j:78.3=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -74,7 +74,7 @@ commons-logging:commons-logging:1.2=deploy_jar,runtimeClasspath,testRuntimeClass info.picocli:picocli:4.7.7=checkstyle io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.16=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.github.java-diff-utils:java-diff-utils:4.17=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-alts:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-api:1.70.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-auth:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -90,16 +90,16 @@ io.grpc:grpc-services:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-stub:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-util:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-xds:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-buffer:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec-http:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-common:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-handler:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-resolver:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-buffer:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-http:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-common:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-handler:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-resolver:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.netty:netty-tcnative-boringssl-static:2.0.77.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath io.netty:netty-tcnative-classes:2.0.77.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-transport-native-unix-common:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport-native-unix-common:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opencensus:opencensus-api:0.31.1=deploy_jar,runtimeClasspath,testRuntimeClasspath io.opencensus:opencensus-contrib-http-util:0.31.1=deploy_jar,runtimeClasspath,testRuntimeClasspath io.perfmark:perfmark-api:0.27.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -147,6 +147,7 @@ org.codehaus.plexus:plexus-utils:3.3.0=checkstyle org.conscrypt:conscrypt-openjdk-uber:2.5.2=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:angus-activation:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:jakarta.mail:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath +org.freemarker:freemarker:2.3.34=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath org.jacoco:org.jacoco.agent:0.8.14=jacocoAgent,jacocoAnt org.jacoco:org.jacoco.ant:0.8.14=jacocoAnt diff --git a/prober/gradle.lockfile b/prober/gradle.lockfile index 9da461335..014c5bd6f 100644 --- a/prober/gradle.lockfile +++ b/prober/gradle.lockfile @@ -60,9 +60,9 @@ com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,compileClasspath,de com.google.monitoring-client:contrib:1.0.7=testCompileClasspath,testRuntimeClasspath com.google.monitoring-client:metrics:1.0.7=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.oauth-client:google-oauth-client:1.36.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.protobuf:protobuf-java-util:4.35.0-RC2=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java-util:4.35.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,testAnnotationProcessor -com.google.protobuf:protobuf-java:4.35.0-RC2=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java:4.35.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.re2j:re2j:1.1=compileClasspath,testCompileClasspath com.google.re2j:re2j:1.7=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.truth:truth:1.4.5=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -78,7 +78,7 @@ commons-logging:commons-logging:1.2=deploy_jar,runtimeClasspath,testRuntimeClass info.picocli:picocli:4.7.7=checkstyle io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.16=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.github.java-diff-utils:java-diff-utils:4.17=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-alts:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-api:1.70.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-auth:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -94,16 +94,16 @@ io.grpc:grpc-services:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-stub:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-util:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-xds:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-buffer:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec-http:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-common:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-handler:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-resolver:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-buffer:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-http:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-common:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-handler:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-resolver:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.netty:netty-tcnative-boringssl-static:2.0.77.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.netty:netty-tcnative-classes:2.0.77.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport-native-unix-common:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport-native-unix-common:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opencensus:opencensus-api:0.31.1=deploy_jar,runtimeClasspath,testRuntimeClasspath io.opencensus:opencensus-contrib-http-util:0.31.1=deploy_jar,runtimeClasspath,testRuntimeClasspath io.perfmark:perfmark-api:0.27.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -153,6 +153,7 @@ org.codehaus.plexus:plexus-utils:3.3.0=checkstyle org.conscrypt:conscrypt-openjdk-uber:2.5.2=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:angus-activation:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:jakarta.mail:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath +org.freemarker:freemarker:2.3.34=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath org.jacoco:org.jacoco.agent:0.8.14=jacocoAgent,jacocoAnt org.jacoco:org.jacoco.ant:0.8.14=jacocoAnt diff --git a/proxy/gradle.lockfile b/proxy/gradle.lockfile index ade45080c..89f11f5a0 100644 --- a/proxy/gradle.lockfile +++ b/proxy/gradle.lockfile @@ -25,7 +25,7 @@ com.google.api:api-common:2.47.1=compileClasspath,deploy_jar,runtimeClasspath,te com.google.api:gax-grpc:2.64.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.api:gax-httpjson:2.64.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.api:gax:2.64.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.apis:google-api-services-cloudkms:v1-rev20260319-2.0.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.apis:google-api-services-cloudkms:v1-rev20260506-2.0.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-monitoring:v3-rev20260129-2.0.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-storage:v1-rev20250312-2.0.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.auth:google-auth-library-credentials:1.33.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -96,7 +96,7 @@ commons-logging:commons-logging:1.2=compileClasspath,deploy_jar,runtimeClasspath info.picocli:picocli:4.7.7=checkstyle io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.16=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.github.java-diff-utils:java-diff-utils:4.17=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-alts:1.70.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-api:1.70.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-auth:1.70.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -114,16 +114,16 @@ io.grpc:grpc-services:1.70.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-stub:1.70.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-util:1.70.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-xds:1.70.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-buffer:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec-http:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-common:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-handler:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-resolver:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-buffer:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-http:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-common:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-handler:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-resolver:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.netty:netty-tcnative-boringssl-static:2.0.77.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.netty:netty-tcnative-classes:2.0.77.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport-native-unix-common:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport-native-unix-common:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opencensus:opencensus-api:0.31.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opencensus:opencensus-contrib-http-util:0.31.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opentelemetry.contrib:opentelemetry-gcp-resources:1.37.0-alpha=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -183,6 +183,7 @@ org.codehaus.plexus:plexus-utils:3.3.0=checkstyle org.conscrypt:conscrypt-openjdk-uber:2.5.2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.eclipse.angus:angus-activation:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:jakarta.mail:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath +org.freemarker:freemarker:2.3.34=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath org.jacoco:org.jacoco.agent:0.8.14=jacocoAgent,jacocoAnt org.jacoco:org.jacoco.ant:0.8.14=jacocoAnt diff --git a/util/gradle.lockfile b/util/gradle.lockfile index b5e0142c9..f8d9a9e91 100644 --- a/util/gradle.lockfile +++ b/util/gradle.lockfile @@ -56,9 +56,9 @@ com.google.http-client:google-http-client:2.1.0=compileClasspath,deploy_jar,runt com.google.j2objc:j2objc-annotations:3.0.0=checkstyle com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,compileClasspath,deploy_jar,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath com.google.oauth-client:google-oauth-client:1.36.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.protobuf:protobuf-java-util:4.35.0-RC2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java-util:4.35.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,testAnnotationProcessor -com.google.protobuf:protobuf-java:4.35.0-RC2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java:4.35.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.re2j:re2j:1.7=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.re2j:re2j:1.8=compileClasspath,testCompileClasspath com.google.truth:truth:1.4.5=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -74,7 +74,7 @@ commons-logging:commons-logging:1.2=compileClasspath,deploy_jar,runtimeClasspath info.picocli:picocli:4.7.7=checkstyle io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.16=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.github.java-diff-utils:java-diff-utils:4.17=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-alts:1.68.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-api:1.68.0=compileClasspath,testCompileClasspath io.grpc:grpc-api:1.70.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -139,6 +139,7 @@ org.codehaus.plexus:plexus-utils:3.3.0=checkstyle org.conscrypt:conscrypt-openjdk-uber:2.5.2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.eclipse.angus:angus-activation:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:jakarta.mail:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath +org.freemarker:freemarker:2.3.34=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:3.0=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:3.0=testCompileClasspath,testRuntimeClasspath org.jacoco:org.jacoco.agent:0.8.14=jacocoAgent,jacocoAnt