mirror of
https://github.com/google/nomulus
synced 2026-06-09 16:33:02 +00:00
Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0fcf26def0 | |||
| 3d88ba4e1b | |||
| 580a3b6981 | |||
| 6990d6058f | |||
| dfeed63c40 | |||
| 9eac9621cb | |||
| b7efc5dd25 | |||
| 1911c11623 | |||
| b8df0bac24 | |||
| 0561c7754e | |||
| 904f16c8b5 | |||
| 3a7d71e411 | |||
| 1ded33ecea | |||
| bf4659f11c | |||
| bac1998d6a | |||
| 4a34369ba9 | |||
| db7d49801d | |||
| 0c52d209e5 | |||
| 73b98d298b | |||
| 7880aab386 | |||
| fa9134328a | |||
| 5b452bf074 | |||
| f749236500 | |||
| e7825fae66 | |||
| 91155d6c67 | |||
| 2ff1026cfd | |||
| f1c46b8030 | |||
| d663bf4db5 | |||
| acf0baf048 | |||
| 2998b56982 | |||
| 7b602300d8 | |||
| fe760d7066 | |||
| ad06f265a5 | |||
| fa9400ebc5 | |||
| 519a85af85 | |||
| b2df127dc4 | |||
| b21042bda9 | |||
| 36378f6b10 | |||
| d01f1f7604 | |||
| e9610636e4 | |||
| d09fc7ee05 | |||
| 0545375eba | |||
| 8a045aedd0 | |||
| 560bec1e83 | |||
| 3e7ea75b6f | |||
| 6ed7e00b00 | |||
| 6e1231233e | |||
| 3098048fdb | |||
| f2846fc914 | |||
| 499237ac57 | |||
| 6bd50421bc | |||
| dbdd2b4491 | |||
| f83f8f92a3 | |||
| 28d3af0ee9 | |||
| 08a6a333ad | |||
| adafab60c4 | |||
| fec806ef8b | |||
| e8ff4081a9 | |||
| 9283cd263f | |||
| 429bc8e6d2 | |||
| fc0a9160b2 | |||
| 3cd0b4d5e5 | |||
| cbe216ced2 | |||
| 1be0ad885c | |||
| 8b819a27e7 | |||
| ec6157b6ac | |||
| 05ed4fd849 | |||
| ecf1721755 | |||
| 7bb69e50c5 |
@@ -1,8 +1,8 @@
|
||||
# Nomulus
|
||||
|
||||
| Internal Build | FOSS Build | LGTM | License | Code Search |
|
||||
|----------------|------------|------|---------|-------------|
|
||||
|[](https://storage.googleapis.com/domain-registry-kokoro/internal/index.html)|[](https://storage.googleapis.com/domain-registry-kokoro/foss/index.html)|[](https://lgtm.com/projects/g/google/nomulus/alerts/)|[](https://github.com/google/nomulus/blob/master/LICENSE)|[](https://sourcegraph.com/github.com/google/nomulus)|
|
||||
|:--------------:|:----------:|:----:|:-------:|:-----------:|
|
||||
|[](https://storage.googleapis.com/domain-registry-kokoro/internal/index.html)|[](https://storage.googleapis.com/domain-registry-kokoro/foss/index.html)|[](https://lgtm.com/projects/g/google/nomulus/alerts/)|[](https://github.com/google/nomulus/blob/master/LICENSE)|[](https://cs.opensource.google/nomulus/nomulus)|
|
||||
|
||||

|
||||
|
||||
|
||||
+3
-3
@@ -169,10 +169,10 @@ allprojects {
|
||||
if (project.name == 'services') return
|
||||
|
||||
repositories {
|
||||
if (rootProject.mavenUrl) {
|
||||
if (!mavenUrl.isEmpty()) {
|
||||
maven {
|
||||
println "Java dependencies: Using repo $pluginsUrl..."
|
||||
url rootProject.mavenUrl
|
||||
println "Java dependencies: Using repo ${mavenUrl}..."
|
||||
url mavenUrl
|
||||
}
|
||||
} else {
|
||||
println "Java dependencies: Using Maven Central..."
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
|
||||
buildscript {
|
||||
if (project.enableDependencyLocking.toBoolean()) {
|
||||
// Lock buildscript dependencies.
|
||||
@@ -40,13 +42,13 @@ if (rootProject.enableDependencyLocking.toBoolean()) {
|
||||
}
|
||||
|
||||
repositories {
|
||||
if (project.ext.properties.mavenUrl == null) {
|
||||
println "Plugin dependencies: Using Maven central..."
|
||||
if (isNullOrEmpty(project.ext.properties.mavenUrl)) {
|
||||
println "Java dependencies: Using Maven central..."
|
||||
mavenCentral()
|
||||
google()
|
||||
} else {
|
||||
maven {
|
||||
println "Plugin dependencies: Using repo ${mavenUrl}..."
|
||||
println "Java dependencies: Using repo ${mavenUrl}..."
|
||||
url mavenUrl
|
||||
}
|
||||
}
|
||||
@@ -82,6 +84,7 @@ dependencies {
|
||||
testCompile deps['com.google.truth:truth']
|
||||
testCompile deps['com.google.truth.extensions:truth-java8-extension']
|
||||
testCompile deps['junit:junit']
|
||||
testCompile deps['org.junit.jupiter:junit-jupiter-api']
|
||||
testCompile deps['org.junit.jupiter:junit-jupiter-engine']
|
||||
testCompile deps['org.junit.vintage:junit-vintage-engine']
|
||||
testCompile deps['org.mockito:mockito-core']
|
||||
|
||||
@@ -5,14 +5,14 @@ antlr:antlr:2.7.7
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.3.2
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:28.0-jre
|
||||
com.google.guava:guava:28.1-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
com.puppycrawl.tools:checkstyle:8.24
|
||||
com.puppycrawl.tools:checkstyle:8.27
|
||||
commons-beanutils:commons-beanutils:1.9.4
|
||||
commons-collections:commons-collections:3.2.2
|
||||
info.picocli:picocli:4.0.3
|
||||
net.sf.saxon:Saxon-HE:9.9.1-4
|
||||
info.picocli:picocli:4.1.1
|
||||
net.sf.saxon:Saxon-HE:9.9.1-5
|
||||
org.antlr:antlr4-runtime:4.7.2
|
||||
org.checkerframework:checker-qual:2.8.1
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.17
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18
|
||||
|
||||
@@ -61,12 +61,12 @@ org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.10.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.json:json:20160212
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.0
|
||||
org.junit.platform:junit-platform-commons:1.6.0
|
||||
org.junit.platform:junit-platform-engine:1.6.0
|
||||
org.junit.vintage:junit-vintage-engine:5.6.0
|
||||
org.junit:junit-bom:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.1
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.1
|
||||
org.junit.platform:junit-platform-commons:1.6.1
|
||||
org.junit.platform:junit-platform-engine:1.6.1
|
||||
org.junit.vintage:junit-vintage-engine:5.6.1
|
||||
org.junit:junit-bom:5.6.1
|
||||
org.mockito:mockito-core:2.25.0
|
||||
org.objenesis:objenesis:2.6
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
|
||||
@@ -61,12 +61,12 @@ org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.10.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.json:json:20160212
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.0
|
||||
org.junit.platform:junit-platform-commons:1.6.0
|
||||
org.junit.platform:junit-platform-engine:1.6.0
|
||||
org.junit.vintage:junit-vintage-engine:5.6.0
|
||||
org.junit:junit-bom:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.1
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.1
|
||||
org.junit.platform:junit-platform-commons:1.6.1
|
||||
org.junit.platform:junit-platform-engine:1.6.1
|
||||
org.junit.vintage:junit-vintage-engine:5.6.1
|
||||
org.junit:junit-bom:5.6.1
|
||||
org.mockito:mockito-core:2.25.0
|
||||
org.objenesis:objenesis:2.6
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
|
||||
@@ -61,12 +61,12 @@ org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.10.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.json:json:20160212
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.0
|
||||
org.junit.platform:junit-platform-commons:1.6.0
|
||||
org.junit.platform:junit-platform-engine:1.6.0
|
||||
org.junit.vintage:junit-vintage-engine:5.6.0
|
||||
org.junit:junit-bom:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.1
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.1
|
||||
org.junit.platform:junit-platform-commons:1.6.1
|
||||
org.junit.platform:junit-platform-engine:1.6.1
|
||||
org.junit.vintage:junit-vintage-engine:5.6.1
|
||||
org.junit:junit-bom:5.6.1
|
||||
org.mockito:mockito-core:2.25.0
|
||||
org.objenesis:objenesis:2.6
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
|
||||
@@ -61,12 +61,12 @@ org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.10.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.json:json:20160212
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.0
|
||||
org.junit.platform:junit-platform-commons:1.6.0
|
||||
org.junit.platform:junit-platform-engine:1.6.0
|
||||
org.junit.vintage:junit-vintage-engine:5.6.0
|
||||
org.junit:junit-bom:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.1
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.1
|
||||
org.junit.platform:junit-platform-commons:1.6.1
|
||||
org.junit.platform:junit-platform-engine:1.6.1
|
||||
org.junit.vintage:junit-vintage-engine:5.6.1
|
||||
org.junit:junit-bom:5.6.1
|
||||
org.mockito:mockito-core:2.25.0
|
||||
org.objenesis:objenesis:2.6
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
|
||||
@@ -62,6 +62,7 @@ dependencies {
|
||||
testingCompile deps['io.github.java-diff-utils:java-diff-utils']
|
||||
|
||||
testCompile deps['junit:junit']
|
||||
testCompile deps['org.junit.jupiter:junit-jupiter-api']
|
||||
testCompile deps['org.junit.jupiter:junit-jupiter-engine']
|
||||
testCompile deps['org.junit.vintage:junit-vintage-engine']
|
||||
}
|
||||
|
||||
@@ -5,14 +5,14 @@ antlr:antlr:2.7.7
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.3.2
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:28.0-jre
|
||||
com.google.guava:guava:28.1-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
com.puppycrawl.tools:checkstyle:8.24
|
||||
com.puppycrawl.tools:checkstyle:8.27
|
||||
commons-beanutils:commons-beanutils:1.9.4
|
||||
commons-collections:commons-collections:3.2.2
|
||||
info.picocli:picocli:4.0.3
|
||||
net.sf.saxon:Saxon-HE:9.9.1-4
|
||||
info.picocli:picocli:4.1.1
|
||||
net.sf.saxon:Saxon-HE:9.9.1-5
|
||||
org.antlr:antlr4-runtime:4.7.2
|
||||
org.checkerframework:checker-qual:2.8.1
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.17
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18
|
||||
|
||||
@@ -20,10 +20,10 @@ org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.10.0
|
||||
org.eclipse.jgit:org.eclipse.jgit:4.4.1.201607150455-r
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.0
|
||||
org.junit.platform:junit-platform-commons:1.6.0
|
||||
org.junit.platform:junit-platform-engine:1.6.0
|
||||
org.junit.vintage:junit-vintage-engine:5.6.0
|
||||
org.junit:junit-bom:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.1
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.1
|
||||
org.junit.platform:junit-platform-commons:1.6.1
|
||||
org.junit.platform:junit-platform-engine:1.6.1
|
||||
org.junit.vintage:junit-vintage-engine:5.6.1
|
||||
org.junit:junit-bom:5.6.1
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
|
||||
@@ -20,10 +20,10 @@ org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.10.0
|
||||
org.eclipse.jgit:org.eclipse.jgit:4.4.1.201607150455-r
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.0
|
||||
org.junit.platform:junit-platform-commons:1.6.0
|
||||
org.junit.platform:junit-platform-engine:1.6.0
|
||||
org.junit.vintage:junit-vintage-engine:5.6.0
|
||||
org.junit:junit-bom:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.1
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.1
|
||||
org.junit.platform:junit-platform-commons:1.6.1
|
||||
org.junit.platform:junit-platform-engine:1.6.1
|
||||
org.junit.vintage:junit-vintage-engine:5.6.1
|
||||
org.junit:junit-bom:5.6.1
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
|
||||
@@ -21,10 +21,10 @@ org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.10.0
|
||||
org.eclipse.jgit:org.eclipse.jgit:4.4.1.201607150455-r
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.0
|
||||
org.junit.platform:junit-platform-commons:1.6.0
|
||||
org.junit.platform:junit-platform-engine:1.6.0
|
||||
org.junit.vintage:junit-vintage-engine:5.6.0
|
||||
org.junit:junit-bom:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.1
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.1
|
||||
org.junit.platform:junit-platform-commons:1.6.1
|
||||
org.junit.platform:junit-platform-engine:1.6.1
|
||||
org.junit.vintage:junit-vintage-engine:5.6.1
|
||||
org.junit:junit-bom:5.6.1
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
|
||||
@@ -21,10 +21,10 @@ org.checkerframework:checker-compat-qual:2.5.5
|
||||
org.checkerframework:checker-qual:2.10.0
|
||||
org.eclipse.jgit:org.eclipse.jgit:4.4.1.201607150455-r
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.0
|
||||
org.junit.platform:junit-platform-commons:1.6.0
|
||||
org.junit.platform:junit-platform-engine:1.6.0
|
||||
org.junit.vintage:junit-vintage-engine:5.6.0
|
||||
org.junit:junit-bom:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.1
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.1
|
||||
org.junit.platform:junit-platform-commons:1.6.1
|
||||
org.junit.platform:junit-platform-engine:1.6.1
|
||||
org.junit.vintage:junit-vintage-engine:5.6.1
|
||||
org.junit:junit-bom:5.6.1
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
# Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Script to generate dr-build and the properties file.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import attr
|
||||
import io
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import List, Union
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class Property:
|
||||
name : str = ''
|
||||
desc : str = ''
|
||||
default : str = ''
|
||||
constraints : type = str
|
||||
|
||||
def validate(self, value: str):
|
||||
"""Verify that "value" is appropriate for the property."""
|
||||
if type is bool:
|
||||
if value not in ('true', 'false'):
|
||||
raise ValidationError('value of {self.name} must be "true" or '
|
||||
'"false".')
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class GradleFlag:
|
||||
flags : Union[str, List[str]]
|
||||
desc : str
|
||||
has_arg : bool = False
|
||||
|
||||
|
||||
PROPERTIES_HEADER = """\
|
||||
# This file defines properties used by the gradle build. It must be kept in
|
||||
# sync with config/nom_build.py.
|
||||
#
|
||||
# To regenerate, run config/nom_build.py --generate-gradle-properties
|
||||
#
|
||||
# To view property descriptions (which are command line flags for
|
||||
# nom_build), run config/nom_build.py --help.
|
||||
#
|
||||
# DO NOT EDIT THIS FILE BY HAND
|
||||
org.gradle.jvmargs=-Xmx1024m
|
||||
"""
|
||||
|
||||
# Define all of our special gradle properties here.
|
||||
PROPERTIES = [
|
||||
Property('mavenUrl',
|
||||
'URL to use for the main maven repository (defaults to maven '
|
||||
'central). This can be http(s) or a "gcs" repo.'),
|
||||
Property('pluginsUrl',
|
||||
'URL to use for the gradle plugins repository (defaults to maven '
|
||||
'central, see also mavenUrl'),
|
||||
Property('uploaderDestination',
|
||||
'Location to upload test reports to. Normally this should be a '
|
||||
'GCS url (see also uploaderCredentialsFile)'),
|
||||
Property('uploaderCredentialsFile',
|
||||
'json credentials file to use to upload test reports.'),
|
||||
Property('uploaderMultithreadedUpload',
|
||||
'Whether to enable multithread upload.'),
|
||||
Property('verboseTestOutput',
|
||||
'If true, show all test output in near-realtime.',
|
||||
'false',
|
||||
bool),
|
||||
Property('flowDocsFile',
|
||||
'Output filename for the flowDocsTool command.'),
|
||||
Property('enableDependencyLocking',
|
||||
'Enables dependency locking.',
|
||||
'true',
|
||||
bool),
|
||||
Property('enableCrossReferencing',
|
||||
'generate metadata during java compile (used for kythe source '
|
||||
'reference generation).',
|
||||
'false'),
|
||||
Property('testFilter',
|
||||
'Comma separated list of test patterns, if specified run only '
|
||||
'these.'),
|
||||
Property('environment', 'GAE Environment for deployment and staging.'),
|
||||
|
||||
# Cloud SQL properties
|
||||
Property('dbServer',
|
||||
'A registry environment name (e.g., "alpha") or a host[:port] '
|
||||
'string'),
|
||||
Property('dbName',
|
||||
'Database name to use in connection.',
|
||||
'postgres'),
|
||||
Property('dbUser', 'Database user name for use in connection'),
|
||||
Property('dbPassword', 'Database password for use in connection'),
|
||||
|
||||
Property('publish_repo',
|
||||
'Maven repository that hosts the Cloud SQL schema jar and the '
|
||||
'registry server test jars. Such jars are needed for '
|
||||
'server/schema integration tests. Please refer to <a '
|
||||
'href="./integration/README.md">integration project</a> for more '
|
||||
'information.'),
|
||||
Property('schema_version',
|
||||
'The nomulus version tag of the schema for use in a database'
|
||||
'integration test.'),
|
||||
Property('nomulus_version',
|
||||
'The version of nomulus to test against in a database '
|
||||
'integration test.'),
|
||||
]
|
||||
|
||||
GRADLE_FLAGS = [
|
||||
GradleFlag(['-a', '--no-rebuild'],
|
||||
'Do not rebuild project dependencies.'),
|
||||
GradleFlag(['-b', '--build-file'], 'Specify the build file.', True),
|
||||
GradleFlag(['--build-cache'],
|
||||
'Enables the Gradle build cache. Gradle will try to reuse '
|
||||
'outputs from previous builds.'),
|
||||
GradleFlag(['-c', '--settings-file'], 'Specify the settings file.', True),
|
||||
GradleFlag(['--configure-on-demand'],
|
||||
'Configure necessary projects only. Gradle will attempt to '
|
||||
'reduce configuration time for large multi-project builds. '
|
||||
'[incubating]'),
|
||||
GradleFlag(['--console'],
|
||||
'Specifies which type of console output to generate. Values '
|
||||
"are 'plain', 'auto' (default), 'rich' or 'verbose'.",
|
||||
True),
|
||||
GradleFlag(['--continue'], 'Continue task execution after a task failure.'),
|
||||
GradleFlag(['-D', '--system-prop'],
|
||||
'Set system property of the JVM (e.g. -Dmyprop=myvalue).',
|
||||
True),
|
||||
GradleFlag(['-d', '--debug'],
|
||||
'Log in debug mode (includes normal stacktrace).'),
|
||||
GradleFlag(['--daemon'],
|
||||
'Uses the Gradle Daemon to run the build. Starts the Daemon '
|
||||
'if not running.'),
|
||||
GradleFlag(['--foreground'], 'Starts the Gradle Daemon in the foreground.'),
|
||||
GradleFlag(['-g', '--gradle-user-home'],
|
||||
'Specifies the gradle user home directory.',
|
||||
True),
|
||||
GradleFlag(['-I', '--init-script'], 'Specify an initialization script.',
|
||||
True),
|
||||
GradleFlag(['-i', '--info'], 'Set log level to info.'),
|
||||
GradleFlag(['--include-build'],
|
||||
'Include the specified build in the composite.',
|
||||
True),
|
||||
GradleFlag(['-m', '--dry-run'],
|
||||
'Run the builds with all task actions disabled.'),
|
||||
GradleFlag(['--max-workers'],
|
||||
'Configure the number of concurrent workers Gradle is '
|
||||
'allowed to use.',
|
||||
True),
|
||||
GradleFlag(['--no-build-cache'], 'Disables the Gradle build cache.'),
|
||||
GradleFlag(['--no-configure-on-demand'],
|
||||
'Disables the use of configuration on demand. [incubating]'),
|
||||
GradleFlag(['--no-daemon'],
|
||||
'Do not use the Gradle daemon to run the build. Useful '
|
||||
'occasionally if you have configured Gradle to always run '
|
||||
'with the daemon by default.'),
|
||||
GradleFlag(['--no-parallel'],
|
||||
'Disables parallel execution to build projects.'),
|
||||
GradleFlag(['--no-scan'],
|
||||
'Disables the creation of a build scan. For more information '
|
||||
'about build scans, please visit '
|
||||
'https://gradle.com/build-scans.'),
|
||||
GradleFlag(['--offline'],
|
||||
'Execute the build without accessing network resources.'),
|
||||
GradleFlag(['-P', '--project-prop'],
|
||||
'Set project property for the build script (e.g. '
|
||||
'-Pmyprop=myvalue).',
|
||||
True),
|
||||
GradleFlag(['-p', '--project-dir'],
|
||||
'Specifies the start directory for Gradle. Defaults to '
|
||||
'current directory.'),
|
||||
GradleFlag(['--parallel'],
|
||||
'Build projects in parallel. Gradle will attempt to '
|
||||
'determine the optimal number of executor threads to use.'),
|
||||
GradleFlag(['--priority'],
|
||||
'Specifies the scheduling priority for the Gradle daemon and '
|
||||
"all processes launched by it. Values are 'normal' (default) "
|
||||
"or 'low' [incubating]",
|
||||
True),
|
||||
GradleFlag(['--profile'],
|
||||
'Profile build execution time and generates a report in the '
|
||||
'<build_dir>/reports/profile directory.'),
|
||||
GradleFlag(['--project-cache-dir'],
|
||||
'Specify the project-specific cache directory. Defaults to '
|
||||
'.gradle in the root project directory.',
|
||||
True),
|
||||
GradleFlag(['-q', '--quiet'], 'Log errors only.'),
|
||||
GradleFlag(['--refresh-dependencies'], 'Refresh the state of dependencies.'),
|
||||
GradleFlag(['--rerun-tasks'], 'Ignore previously cached task results.'),
|
||||
GradleFlag(['-S', '--full-stacktrace'],
|
||||
'Print out the full (very verbose) stacktrace for all '
|
||||
'exceptions.'),
|
||||
GradleFlag(['-s', '--stacktrace'],
|
||||
'Print out the stacktrace for all exceptions.'),
|
||||
GradleFlag(['--scan'],
|
||||
'Creates a build scan. Gradle will emit a warning if the '
|
||||
'build scan plugin has not been applied. '
|
||||
'(https://gradle.com/build-scans)'),
|
||||
GradleFlag(['--status'],
|
||||
'Shows status of running and recently stopped Gradle '
|
||||
'Daemon(s).'),
|
||||
GradleFlag(['--stop'], 'Stops the Gradle Daemon if it is running.'),
|
||||
GradleFlag(['-t', '--continuous'],
|
||||
'Enables continuous build. Gradle does not exit and will '
|
||||
're-execute tasks when task file inputs change.'),
|
||||
GradleFlag(['--update-locks'],
|
||||
'Perform a partial update of the dependency lock, letting '
|
||||
'passed in module notations change version. [incubating]'),
|
||||
GradleFlag(['-v', '--version'], 'Print version info.'),
|
||||
GradleFlag(['-w', '--warn'], 'Set log level to warn.'),
|
||||
GradleFlag(['--warning-mode'],
|
||||
'Specifies which mode of warnings to generate. Values are '
|
||||
"'all', 'fail', 'summary'(default) or 'none'",
|
||||
True),
|
||||
GradleFlag(['--write-locks'],
|
||||
'Persists dependency resolution for locked configurations, '
|
||||
'ignoring existing locking information if it exists '
|
||||
'[incubating]'),
|
||||
GradleFlag(['-x', '--exclude-task'],
|
||||
'Specify a task to be excluded from execution.',
|
||||
True),
|
||||
]
|
||||
def generate_gradle_properties() -> str:
|
||||
"""Returns the expected contents of gradle.properties."""
|
||||
out = io.StringIO()
|
||||
out.write(PROPERTIES_HEADER)
|
||||
|
||||
for prop in PROPERTIES:
|
||||
out.write(f'{prop.name}={prop.default}\n')
|
||||
|
||||
return out.getvalue()
|
||||
|
||||
|
||||
def get_root() -> str:
|
||||
"""Returns the root of the nomulus build tree."""
|
||||
cur_dir = os.getcwd()
|
||||
if not os.path.exists(os.path.join(cur_dir, '.git')) or \
|
||||
not os.path.exists(os.path.join(cur_dir, 'core')) or \
|
||||
not os.path.exists(os.path.join(cur_dir, 'gradle.properties')):
|
||||
raise Exception('You must run this script from the root directory')
|
||||
return cur_dir
|
||||
|
||||
|
||||
def main(args):
|
||||
parser = argparse.ArgumentParser('nom_build')
|
||||
for prop in PROPERTIES:
|
||||
parser.add_argument('--' + prop.name, default=prop.default,
|
||||
help=prop.desc)
|
||||
|
||||
# Add Gradle flags. We set 'dest' to the first flag to get a name that is
|
||||
# predictable for getattr (even though it will have a leading '-' and thus
|
||||
# we can't use normal python attribute syntax to get it).
|
||||
for flag in GRADLE_FLAGS:
|
||||
if flag.has_arg:
|
||||
parser.add_argument(*flag.flags, dest=flag.flags[0],
|
||||
help=flag.desc)
|
||||
else:
|
||||
parser.add_argument(*flag.flags, dest=flag.flags[0],
|
||||
help=flag.desc,
|
||||
action='store_true')
|
||||
|
||||
# Add a flag to regenerate the gradle properties file.
|
||||
parser.add_argument('--generate-gradle-properties',
|
||||
help='Regenerate the gradle.properties file. This '
|
||||
'file must be regenerated when changes are made to '
|
||||
'config/nom_build.py, and should not be updated by '
|
||||
'hand.',
|
||||
action='store_true')
|
||||
|
||||
# Consume the remaining non-flag arguments.
|
||||
parser.add_argument('non_flag_args', nargs='*')
|
||||
|
||||
# Parse command line arguments. Note that this exits the program and
|
||||
# prints usage if either of the help options (-h, --help) are specified.
|
||||
args = parser.parse_args(args)
|
||||
|
||||
gradle_properties = generate_gradle_properties()
|
||||
root = get_root()
|
||||
|
||||
# If we're regenerating properties, do so and exit.
|
||||
if args.generate_gradle_properties:
|
||||
with open(f'{root}/gradle.properties', 'w') as dst:
|
||||
dst.write(gradle_properties)
|
||||
return
|
||||
|
||||
# Verify that the gradle properties file is what we expect it to be.
|
||||
with open(f'{root}/gradle.properties') as src:
|
||||
if src.read() != gradle_properties:
|
||||
print('\033[33mWARNING:\033[0m Gradle properties out of sync '
|
||||
'with nom_build. Run with --generate-gradle-properties '
|
||||
'to regenerate.')
|
||||
|
||||
# Add properties to the gradle argument list.
|
||||
gradle_command = [f'{root}/gradlew']
|
||||
for prop in PROPERTIES:
|
||||
arg_val = getattr(args, prop.name)
|
||||
if arg_val != prop.default:
|
||||
prop.validate(arg_val)
|
||||
gradle_command.extend(['-P', f'{prop.name}={arg_val}'])
|
||||
|
||||
# Add Gradle flags to the gradle argument list.
|
||||
for flag in GRADLE_FLAGS:
|
||||
arg_val = getattr(args, flag.flags[0])
|
||||
if arg_val:
|
||||
gradle_command.append(flag.flags[-1])
|
||||
if flag.has_arg:
|
||||
gradle_command.append(arg_val)
|
||||
|
||||
# Add the non-flag args (we exclude the first, which is the command name
|
||||
# itself) and run.
|
||||
gradle_command.extend(args.non_flag_args[1:])
|
||||
subprocess.call(gradle_command)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
# Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import io
|
||||
import os
|
||||
import unittest
|
||||
from unittest import mock
|
||||
import nom_build
|
||||
import subprocess
|
||||
|
||||
FAKE_PROPERTIES = [
|
||||
nom_build.Property('foo', 'help text'),
|
||||
nom_build.Property('bar', 'more text', 'true', bool),
|
||||
]
|
||||
|
||||
FAKE_PROP_CONTENTS = nom_build.PROPERTIES_HEADER + 'foo=\nbar=true\n'
|
||||
PROPERTIES_FILENAME = '/tmp/rootdir/gradle.properties'
|
||||
GRADLEW = '/tmp/rootdir/gradlew'
|
||||
|
||||
|
||||
class FileFake(io.StringIO):
|
||||
"""File fake that writes file contents to the dictionary on close."""
|
||||
def __init__(self, contents_dict, filename):
|
||||
self.dict = contents_dict
|
||||
self.filename = filename
|
||||
super(FileFake, self).__init__()
|
||||
|
||||
def close(self):
|
||||
self.dict[self.filename] = self.getvalue()
|
||||
super(FileFake, self).close()
|
||||
|
||||
|
||||
class MyTest(unittest.TestCase):
|
||||
|
||||
def open_fake(self, filename, action='r'):
|
||||
if action == 'r':
|
||||
return io.StringIO(self.file_contents.get(filename, ''))
|
||||
elif action == 'w':
|
||||
result = self.file_contents[filename] = (
|
||||
FileFake(self.file_contents, filename))
|
||||
return result
|
||||
else:
|
||||
raise Exception(f'Unexpected action {action}')
|
||||
|
||||
def print_fake(self, data):
|
||||
self.printed.append(data)
|
||||
|
||||
def setUp(self):
|
||||
self.addCleanup(mock.patch.stopall)
|
||||
self.exists_mock = mock.patch.object(os.path, 'exists').start()
|
||||
self.getcwd_mock = mock.patch.object(os, 'getcwd').start()
|
||||
self.getcwd_mock.return_value = '/tmp/rootdir'
|
||||
self.open_mock = (
|
||||
mock.patch.object(nom_build, 'open', self.open_fake).start())
|
||||
self.print_mock = (
|
||||
mock.patch.object(nom_build, 'print', self.print_fake).start())
|
||||
|
||||
self.call_mock = mock.patch.object(subprocess, 'call').start()
|
||||
|
||||
self.file_contents = {
|
||||
# Prefil with the actual file contents.
|
||||
PROPERTIES_FILENAME: nom_build.generate_gradle_properties()
|
||||
}
|
||||
self.printed = []
|
||||
|
||||
@mock.patch.object(nom_build, 'PROPERTIES', FAKE_PROPERTIES)
|
||||
def test_property_generation(self):
|
||||
self.assertEqual(nom_build.generate_gradle_properties(),
|
||||
FAKE_PROP_CONTENTS)
|
||||
|
||||
@mock.patch.object(nom_build, 'PROPERTIES', FAKE_PROPERTIES)
|
||||
def test_property_file_write(self):
|
||||
nom_build.main(['nom_build', '--generate-gradle-properties'])
|
||||
self.assertEqual(self.file_contents[PROPERTIES_FILENAME],
|
||||
FAKE_PROP_CONTENTS)
|
||||
|
||||
def test_property_file_incorrect(self):
|
||||
self.file_contents[PROPERTIES_FILENAME] = 'bad contents'
|
||||
nom_build.main(['nom_build'])
|
||||
self.assertIn('', self.printed[0])
|
||||
|
||||
def test_no_args(self):
|
||||
nom_build.main(['nom_build'])
|
||||
self.assertEqual(self.printed, [])
|
||||
self.call_mock.assert_called_with([GRADLEW])
|
||||
|
||||
def test_property_calls(self):
|
||||
nom_build.main(['nom_build', '--testFilter=foo'])
|
||||
self.call_mock.assert_called_with([GRADLEW, '-P', 'testFilter=foo'])
|
||||
|
||||
def test_gradle_flags(self):
|
||||
nom_build.main(['nom_build', '-d', '-b', 'foo'])
|
||||
self.call_mock.assert_called_with([GRADLEW, '--build-file', 'foo',
|
||||
'--debug'])
|
||||
|
||||
unittest.main()
|
||||
|
||||
|
||||
Binary file not shown.
+23
-1
@@ -177,6 +177,8 @@ dependencies {
|
||||
|
||||
compile deps['com.beust:jcommander']
|
||||
compile deps['com.google.api-client:google-api-client']
|
||||
compile deps['com.google.api-client:google-api-client-appengine']
|
||||
compile deps['com.google.api-client:google-api-client-servlet']
|
||||
compile deps['com.google.monitoring-client:metrics']
|
||||
compile deps['com.google.monitoring-client:stackdriver']
|
||||
compile deps['com.google.api-client:google-api-client-java6']
|
||||
@@ -197,6 +199,7 @@ dependencies {
|
||||
compile deps['com.google.appengine:appengine-remote-api']
|
||||
compile deps['com.google.auth:google-auth-library-credentials']
|
||||
compile deps['com.google.auth:google-auth-library-oauth2-http']
|
||||
compile deps['com.google.cloud.sql:jdbc-socket-factory-core']
|
||||
runtimeOnly deps['com.google.cloud.sql:postgres-socket-factory']
|
||||
compile deps['com.google.code.gson:gson']
|
||||
compile deps['com.google.auto.value:auto-value-annotations']
|
||||
@@ -215,6 +218,8 @@ dependencies {
|
||||
compile deps['com.google.oauth-client:google-oauth-client']
|
||||
compile deps['com.google.oauth-client:google-oauth-client-java6']
|
||||
compile deps['com.google.oauth-client:google-oauth-client-jetty']
|
||||
compile deps['com.google.oauth-client:google-oauth-client-appengine']
|
||||
compile deps['com.google.oauth-client:google-oauth-client-servlet']
|
||||
compile deps['com.google.re2j:re2j']
|
||||
compile deps['com.google.template:soy']
|
||||
compile deps['com.googlecode.json-simple:json-simple']
|
||||
@@ -261,6 +266,7 @@ dependencies {
|
||||
testCompile deps['org.seleniumhq.selenium:selenium-chrome-driver']
|
||||
testCompile deps['org.seleniumhq.selenium:selenium-java']
|
||||
testCompile deps['org.seleniumhq.selenium:selenium-remote-driver']
|
||||
runtimeOnly deps['org.slf4j:slf4j-jdk14']
|
||||
testCompile deps['org.testcontainers:jdbc']
|
||||
compile deps['org.testcontainers:postgresql']
|
||||
testCompile deps['org.testcontainers:selenium']
|
||||
@@ -300,7 +306,11 @@ dependencies {
|
||||
testCompile deps['org.hamcrest:hamcrest-library']
|
||||
compile deps['org.hibernate:hibernate-hikaricp']
|
||||
testCompile deps['junit:junit']
|
||||
testCompile deps['org.junit.jupiter:junit-jupiter-api']
|
||||
testCompile deps['org.junit.jupiter:junit-jupiter-engine']
|
||||
testCompile deps['org.junit.jupiter:junit-jupiter-migrationsupport']
|
||||
testCompile deps['org.junit.platform:junit-platform-runner']
|
||||
testCompile deps['org.junit.platform:junit-platform-suite-api']
|
||||
testCompile deps['org.junit.vintage:junit-vintage-engine']
|
||||
testCompile deps['org.mockito:mockito-core']
|
||||
runtime deps['org.postgresql:postgresql']
|
||||
@@ -638,6 +648,10 @@ artifacts {
|
||||
*/
|
||||
class FilteringTest extends Test {
|
||||
|
||||
FilteringTest() {
|
||||
useJUnitPlatform();
|
||||
}
|
||||
|
||||
private void applyTestFilter() {
|
||||
if (project.testFilter) {
|
||||
testNameIncludePatterns = project.testFilter.split(',')
|
||||
@@ -725,6 +739,10 @@ task registryToolIntegrationTest {
|
||||
|
||||
// Dedicated test suite for schema-dependent tests.
|
||||
task sqlIntegrationTest(type: FilteringTest) {
|
||||
// TestSuite still requires a JUnit 4 runner, which knows how to handle JUnit 5 tests.
|
||||
// Here we need to override parent's choice of JUnit 5. If changing this, remember to
|
||||
// change :integration:sqlIntegrationTest too.
|
||||
useJUnit()
|
||||
excludeTestCases = false
|
||||
tests = ['google/registry/schema/integration/SqlIntegrationTestSuite.*']
|
||||
}
|
||||
@@ -869,17 +887,21 @@ test {
|
||||
// Don't run any tests from this task, all testing gets done in the
|
||||
// FilteringTest tasks.
|
||||
exclude "**"
|
||||
}.dependsOn(fragileTest, outcastTest, standardTest, registryToolIntegrationTest)
|
||||
// TODO(weiminyu): Remove dependency on sqlIntegrationTest
|
||||
}.dependsOn(fragileTest, outcastTest, standardTest, registryToolIntegrationTest, sqlIntegrationTest)
|
||||
|
||||
createUberJar('nomulus', 'nomulus', 'google.registry.tools.RegistryTool')
|
||||
|
||||
// A jar with classes and resources from main sourceSet, excluding internal
|
||||
// data. See comments on configurations.nomulus_test above for details.
|
||||
// TODO(weiminyu): release process should build this using the public repo to eliminate the need
|
||||
// for excludes.
|
||||
task nomulusFossJar (type: Jar) {
|
||||
archiveBaseName = 'nomulus'
|
||||
archiveClassifier = 'public'
|
||||
from (project.sourceSets.main.output) {
|
||||
exclude 'google/registry/config/files/**'
|
||||
exclude 'google/registry/proxy/config/**'
|
||||
}
|
||||
from (project.sourceSets.main.output) {
|
||||
include 'google/registry/config/files/default-config.yaml'
|
||||
|
||||
@@ -5,14 +5,14 @@ antlr:antlr:2.7.7
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.3.2
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:28.0-jre
|
||||
com.google.guava:guava:28.1-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
com.puppycrawl.tools:checkstyle:8.24
|
||||
com.puppycrawl.tools:checkstyle:8.27
|
||||
commons-beanutils:commons-beanutils:1.9.4
|
||||
commons-collections:commons-collections:3.2.2
|
||||
info.picocli:picocli:4.0.3
|
||||
net.sf.saxon:Saxon-HE:9.9.1-4
|
||||
info.picocli:picocli:4.1.1
|
||||
net.sf.saxon:Saxon-HE:9.9.1-5
|
||||
org.antlr:antlr4-runtime:4.7.2
|
||||
org.checkerframework:checker-qual:2.8.1
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.17
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.18
|
||||
|
||||
@@ -11,6 +11,14 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
|
||||
com.fasterxml.jackson.core:jackson-core:2.10.2
|
||||
com.fasterxml.jackson.core:jackson-databind:2.9.10
|
||||
com.fasterxml:classmate:1.3.4
|
||||
com.github.jnr:jffi:1.2.17
|
||||
com.github.jnr:jnr-a64asm:1.0.0
|
||||
com.github.jnr:jnr-constants:0.9.11
|
||||
com.github.jnr:jnr-enxio:0.19
|
||||
com.github.jnr:jnr-ffi:2.1.9
|
||||
com.github.jnr:jnr-posix:3.0.47
|
||||
com.github.jnr:jnr-unixsocket:0.21
|
||||
com.github.jnr:jnr-x86asm:1.0.2
|
||||
com.google.api-client:google-api-client-appengine:1.30.8
|
||||
com.google.api-client:google-api-client-jackson2:1.27.0
|
||||
com.google.api-client:google-api-client-java6:1.27.0
|
||||
@@ -51,6 +59,7 @@ com.google.apis:google-api-services-groupssettings:v1-rev60-1.22.0
|
||||
com.google.apis:google-api-services-monitoring:v3-rev426-1.23.0
|
||||
com.google.apis:google-api-services-pubsub:v1-rev20181105-1.27.0
|
||||
com.google.apis:google-api-services-sheets:v4-rev483-1.22.0
|
||||
com.google.apis:google-api-services-sqladmin:v1beta4-rev56-1.23.0
|
||||
com.google.apis:google-api-services-storage:v1-rev20181109-1.27.0
|
||||
com.google.appengine.tools:appengine-gcs-client:0.6
|
||||
com.google.appengine.tools:appengine-mapreduce:0.9
|
||||
@@ -66,6 +75,7 @@ com.google.cloud.bigdataoss:gcsio:1.9.16
|
||||
com.google.cloud.bigdataoss:util:1.9.16
|
||||
com.google.cloud.bigtable:bigtable-client-core:1.8.0
|
||||
com.google.cloud.datastore:datastore-v1-proto-client:1.6.0
|
||||
com.google.cloud.sql:jdbc-socket-factory-core:1.0.12
|
||||
com.google.cloud:google-cloud-bigquerystorage:0.79.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable-admin:0.73.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable:0.73.0-alpha
|
||||
@@ -213,11 +223,11 @@ org.mockito:mockito-core:1.9.5
|
||||
org.mortbay.jetty:jetty-util:6.1.26
|
||||
org.mortbay.jetty:jetty:6.1.26
|
||||
org.objenesis:objenesis:1.2
|
||||
org.ow2.asm:asm-analysis:6.0
|
||||
org.ow2.asm:asm-analysis:7.0
|
||||
org.ow2.asm:asm-commons:6.0
|
||||
org.ow2.asm:asm-tree:6.0
|
||||
org.ow2.asm:asm-util:6.0
|
||||
org.ow2.asm:asm:6.0
|
||||
org.ow2.asm:asm-tree:7.0
|
||||
org.ow2.asm:asm-util:7.0
|
||||
org.ow2.asm:asm:7.0
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -11,6 +11,14 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
|
||||
com.fasterxml.jackson.core:jackson-core:2.10.2
|
||||
com.fasterxml.jackson.core:jackson-databind:2.9.10
|
||||
com.fasterxml:classmate:1.3.4
|
||||
com.github.jnr:jffi:1.2.17
|
||||
com.github.jnr:jnr-a64asm:1.0.0
|
||||
com.github.jnr:jnr-constants:0.9.11
|
||||
com.github.jnr:jnr-enxio:0.19
|
||||
com.github.jnr:jnr-ffi:2.1.9
|
||||
com.github.jnr:jnr-posix:3.0.47
|
||||
com.github.jnr:jnr-unixsocket:0.21
|
||||
com.github.jnr:jnr-x86asm:1.0.2
|
||||
com.google.api-client:google-api-client-appengine:1.30.8
|
||||
com.google.api-client:google-api-client-jackson2:1.27.0
|
||||
com.google.api-client:google-api-client-java6:1.27.0
|
||||
@@ -51,6 +59,7 @@ com.google.apis:google-api-services-groupssettings:v1-rev60-1.22.0
|
||||
com.google.apis:google-api-services-monitoring:v3-rev426-1.23.0
|
||||
com.google.apis:google-api-services-pubsub:v1-rev20181105-1.27.0
|
||||
com.google.apis:google-api-services-sheets:v4-rev483-1.22.0
|
||||
com.google.apis:google-api-services-sqladmin:v1beta4-rev56-1.23.0
|
||||
com.google.apis:google-api-services-storage:v1-rev20181109-1.27.0
|
||||
com.google.appengine.tools:appengine-gcs-client:0.6
|
||||
com.google.appengine.tools:appengine-mapreduce:0.9
|
||||
@@ -66,6 +75,7 @@ com.google.cloud.bigdataoss:gcsio:1.9.16
|
||||
com.google.cloud.bigdataoss:util:1.9.16
|
||||
com.google.cloud.bigtable:bigtable-client-core:1.8.0
|
||||
com.google.cloud.datastore:datastore-v1-proto-client:1.6.0
|
||||
com.google.cloud.sql:jdbc-socket-factory-core:1.0.12
|
||||
com.google.cloud:google-cloud-bigquerystorage:0.79.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable-admin:0.73.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable:0.73.0-alpha
|
||||
@@ -208,11 +218,11 @@ org.jvnet.staxex:stax-ex:1.8
|
||||
org.mortbay.jetty:jetty-util:6.1.26
|
||||
org.mortbay.jetty:jetty:6.1.26
|
||||
org.objenesis:objenesis:1.2
|
||||
org.ow2.asm:asm-analysis:6.0
|
||||
org.ow2.asm:asm-analysis:7.0
|
||||
org.ow2.asm:asm-commons:6.0
|
||||
org.ow2.asm:asm-tree:6.0
|
||||
org.ow2.asm:asm-util:6.0
|
||||
org.ow2.asm:asm:6.0
|
||||
org.ow2.asm:asm-tree:7.0
|
||||
org.ow2.asm:asm-util:7.0
|
||||
org.ow2.asm:asm:7.0
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -235,6 +235,7 @@ org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
org.scijava:native-lib-loader:2.0.2
|
||||
org.slf4j:slf4j-api:1.7.28
|
||||
org.slf4j:slf4j-jdk14:1.7.28
|
||||
org.testcontainers:database-commons:1.12.1
|
||||
org.testcontainers:jdbc:1.12.1
|
||||
org.testcontainers:postgresql:1.12.1
|
||||
|
||||
@@ -232,6 +232,7 @@ org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
org.scijava:native-lib-loader:2.0.2
|
||||
org.slf4j:slf4j-api:1.7.28
|
||||
org.slf4j:slf4j-jdk14:1.7.28
|
||||
org.testcontainers:database-commons:1.12.1
|
||||
org.testcontainers:jdbc:1.12.1
|
||||
org.testcontainers:postgresql:1.12.1
|
||||
|
||||
@@ -11,6 +11,14 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
|
||||
com.fasterxml.jackson.core:jackson-core:2.10.2
|
||||
com.fasterxml.jackson.core:jackson-databind:2.9.10
|
||||
com.fasterxml:classmate:1.3.4
|
||||
com.github.jnr:jffi:1.2.17
|
||||
com.github.jnr:jnr-a64asm:1.0.0
|
||||
com.github.jnr:jnr-constants:0.9.11
|
||||
com.github.jnr:jnr-enxio:0.19
|
||||
com.github.jnr:jnr-ffi:2.1.9
|
||||
com.github.jnr:jnr-posix:3.0.47
|
||||
com.github.jnr:jnr-unixsocket:0.21
|
||||
com.github.jnr:jnr-x86asm:1.0.2
|
||||
com.google.api-client:google-api-client-appengine:1.30.8
|
||||
com.google.api-client:google-api-client-jackson2:1.27.0
|
||||
com.google.api-client:google-api-client-java6:1.27.0
|
||||
@@ -51,6 +59,7 @@ com.google.apis:google-api-services-groupssettings:v1-rev60-1.22.0
|
||||
com.google.apis:google-api-services-monitoring:v3-rev426-1.23.0
|
||||
com.google.apis:google-api-services-pubsub:v1-rev20181105-1.27.0
|
||||
com.google.apis:google-api-services-sheets:v4-rev483-1.22.0
|
||||
com.google.apis:google-api-services-sqladmin:v1beta4-rev56-1.23.0
|
||||
com.google.apis:google-api-services-storage:v1-rev20181109-1.27.0
|
||||
com.google.appengine.tools:appengine-gcs-client:0.6
|
||||
com.google.appengine.tools:appengine-mapreduce:0.9
|
||||
@@ -66,6 +75,7 @@ com.google.cloud.bigdataoss:gcsio:1.9.16
|
||||
com.google.cloud.bigdataoss:util:1.9.16
|
||||
com.google.cloud.bigtable:bigtable-client-core:1.8.0
|
||||
com.google.cloud.datastore:datastore-v1-proto-client:1.6.0
|
||||
com.google.cloud.sql:jdbc-socket-factory-core:1.0.12
|
||||
com.google.cloud:google-cloud-bigquerystorage:0.79.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable-admin:0.73.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable:0.73.0-alpha
|
||||
@@ -213,11 +223,11 @@ org.mockito:mockito-core:1.9.5
|
||||
org.mortbay.jetty:jetty-util:6.1.26
|
||||
org.mortbay.jetty:jetty:6.1.26
|
||||
org.objenesis:objenesis:1.2
|
||||
org.ow2.asm:asm-analysis:6.0
|
||||
org.ow2.asm:asm-analysis:7.0
|
||||
org.ow2.asm:asm-commons:6.0
|
||||
org.ow2.asm:asm-tree:6.0
|
||||
org.ow2.asm:asm-util:6.0
|
||||
org.ow2.asm:asm:6.0
|
||||
org.ow2.asm:asm-tree:7.0
|
||||
org.ow2.asm:asm-util:7.0
|
||||
org.ow2.asm:asm:7.0
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -11,6 +11,14 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
|
||||
com.fasterxml.jackson.core:jackson-core:2.10.2
|
||||
com.fasterxml.jackson.core:jackson-databind:2.9.10
|
||||
com.fasterxml:classmate:1.3.4
|
||||
com.github.jnr:jffi:1.2.17
|
||||
com.github.jnr:jnr-a64asm:1.0.0
|
||||
com.github.jnr:jnr-constants:0.9.11
|
||||
com.github.jnr:jnr-enxio:0.19
|
||||
com.github.jnr:jnr-ffi:2.1.9
|
||||
com.github.jnr:jnr-posix:3.0.47
|
||||
com.github.jnr:jnr-unixsocket:0.21
|
||||
com.github.jnr:jnr-x86asm:1.0.2
|
||||
com.google.api-client:google-api-client-appengine:1.30.8
|
||||
com.google.api-client:google-api-client-jackson2:1.27.0
|
||||
com.google.api-client:google-api-client-java6:1.27.0
|
||||
@@ -51,6 +59,7 @@ com.google.apis:google-api-services-groupssettings:v1-rev60-1.22.0
|
||||
com.google.apis:google-api-services-monitoring:v3-rev426-1.23.0
|
||||
com.google.apis:google-api-services-pubsub:v1-rev20181105-1.27.0
|
||||
com.google.apis:google-api-services-sheets:v4-rev483-1.22.0
|
||||
com.google.apis:google-api-services-sqladmin:v1beta4-rev56-1.23.0
|
||||
com.google.apis:google-api-services-storage:v1-rev20181109-1.27.0
|
||||
com.google.appengine.tools:appengine-gcs-client:0.6
|
||||
com.google.appengine.tools:appengine-mapreduce:0.9
|
||||
@@ -66,6 +75,7 @@ com.google.cloud.bigdataoss:gcsio:1.9.16
|
||||
com.google.cloud.bigdataoss:util:1.9.16
|
||||
com.google.cloud.bigtable:bigtable-client-core:1.8.0
|
||||
com.google.cloud.datastore:datastore-v1-proto-client:1.6.0
|
||||
com.google.cloud.sql:jdbc-socket-factory-core:1.0.12
|
||||
com.google.cloud:google-cloud-bigquerystorage:0.79.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable-admin:0.73.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable:0.73.0-alpha
|
||||
@@ -211,11 +221,11 @@ org.mockito:mockito-core:1.9.5
|
||||
org.mortbay.jetty:jetty-util:6.1.26
|
||||
org.mortbay.jetty:jetty:6.1.26
|
||||
org.objenesis:objenesis:1.2
|
||||
org.ow2.asm:asm-analysis:6.0
|
||||
org.ow2.asm:asm-analysis:7.0
|
||||
org.ow2.asm:asm-commons:6.0
|
||||
org.ow2.asm:asm-tree:6.0
|
||||
org.ow2.asm:asm-util:6.0
|
||||
org.ow2.asm:asm:6.0
|
||||
org.ow2.asm:asm-tree:7.0
|
||||
org.ow2.asm:asm-util:7.0
|
||||
org.ow2.asm:asm:7.0
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -11,6 +11,14 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
|
||||
com.fasterxml.jackson.core:jackson-core:2.10.2
|
||||
com.fasterxml.jackson.core:jackson-databind:2.9.10
|
||||
com.fasterxml:classmate:1.3.4
|
||||
com.github.jnr:jffi:1.2.17
|
||||
com.github.jnr:jnr-a64asm:1.0.0
|
||||
com.github.jnr:jnr-constants:0.9.11
|
||||
com.github.jnr:jnr-enxio:0.19
|
||||
com.github.jnr:jnr-ffi:2.1.9
|
||||
com.github.jnr:jnr-posix:3.0.47
|
||||
com.github.jnr:jnr-unixsocket:0.21
|
||||
com.github.jnr:jnr-x86asm:1.0.2
|
||||
com.google.api-client:google-api-client-appengine:1.30.8
|
||||
com.google.api-client:google-api-client-jackson2:1.27.0
|
||||
com.google.api-client:google-api-client-java6:1.27.0
|
||||
@@ -51,6 +59,7 @@ com.google.apis:google-api-services-groupssettings:v1-rev60-1.22.0
|
||||
com.google.apis:google-api-services-monitoring:v3-rev426-1.23.0
|
||||
com.google.apis:google-api-services-pubsub:v1-rev20181105-1.27.0
|
||||
com.google.apis:google-api-services-sheets:v4-rev483-1.22.0
|
||||
com.google.apis:google-api-services-sqladmin:v1beta4-rev56-1.23.0
|
||||
com.google.apis:google-api-services-storage:v1-rev20181109-1.27.0
|
||||
com.google.appengine.tools:appengine-gcs-client:0.6
|
||||
com.google.appengine.tools:appengine-mapreduce:0.9
|
||||
@@ -66,6 +75,7 @@ com.google.cloud.bigdataoss:gcsio:1.9.16
|
||||
com.google.cloud.bigdataoss:util:1.9.16
|
||||
com.google.cloud.bigtable:bigtable-client-core:1.8.0
|
||||
com.google.cloud.datastore:datastore-v1-proto-client:1.6.0
|
||||
com.google.cloud.sql:jdbc-socket-factory-core:1.0.12
|
||||
com.google.cloud:google-cloud-bigquerystorage:0.79.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable-admin:0.73.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable:0.73.0-alpha
|
||||
@@ -213,11 +223,11 @@ org.mockito:mockito-core:1.9.5
|
||||
org.mortbay.jetty:jetty-util:6.1.26
|
||||
org.mortbay.jetty:jetty:6.1.26
|
||||
org.objenesis:objenesis:1.2
|
||||
org.ow2.asm:asm-analysis:6.0
|
||||
org.ow2.asm:asm-analysis:7.0
|
||||
org.ow2.asm:asm-commons:6.0
|
||||
org.ow2.asm:asm-tree:6.0
|
||||
org.ow2.asm:asm-util:6.0
|
||||
org.ow2.asm:asm:6.0
|
||||
org.ow2.asm:asm-tree:7.0
|
||||
org.ow2.asm:asm-util:7.0
|
||||
org.ow2.asm:asm:7.0
|
||||
org.postgresql:postgresql:42.2.6
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
|
||||
@@ -11,6 +11,14 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
|
||||
com.fasterxml.jackson.core:jackson-core:2.10.2
|
||||
com.fasterxml.jackson.core:jackson-databind:2.9.10
|
||||
com.fasterxml:classmate:1.3.4
|
||||
com.github.jnr:jffi:1.2.17
|
||||
com.github.jnr:jnr-a64asm:1.0.0
|
||||
com.github.jnr:jnr-constants:0.9.11
|
||||
com.github.jnr:jnr-enxio:0.19
|
||||
com.github.jnr:jnr-ffi:2.1.9
|
||||
com.github.jnr:jnr-posix:3.0.47
|
||||
com.github.jnr:jnr-unixsocket:0.21
|
||||
com.github.jnr:jnr-x86asm:1.0.2
|
||||
com.google.api-client:google-api-client-appengine:1.30.8
|
||||
com.google.api-client:google-api-client-jackson2:1.27.0
|
||||
com.google.api-client:google-api-client-java6:1.27.0
|
||||
@@ -51,6 +59,7 @@ com.google.apis:google-api-services-groupssettings:v1-rev60-1.22.0
|
||||
com.google.apis:google-api-services-monitoring:v3-rev426-1.23.0
|
||||
com.google.apis:google-api-services-pubsub:v1-rev20181105-1.27.0
|
||||
com.google.apis:google-api-services-sheets:v4-rev483-1.22.0
|
||||
com.google.apis:google-api-services-sqladmin:v1beta4-rev56-1.23.0
|
||||
com.google.apis:google-api-services-storage:v1-rev20181109-1.27.0
|
||||
com.google.appengine.tools:appengine-gcs-client:0.6
|
||||
com.google.appengine.tools:appengine-mapreduce:0.9
|
||||
@@ -66,6 +75,7 @@ com.google.cloud.bigdataoss:gcsio:1.9.16
|
||||
com.google.cloud.bigdataoss:util:1.9.16
|
||||
com.google.cloud.bigtable:bigtable-client-core:1.8.0
|
||||
com.google.cloud.datastore:datastore-v1-proto-client:1.6.0
|
||||
com.google.cloud.sql:jdbc-socket-factory-core:1.0.12
|
||||
com.google.cloud:google-cloud-bigquerystorage:0.79.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable-admin:0.73.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable:0.73.0-alpha
|
||||
@@ -213,11 +223,11 @@ org.mockito:mockito-core:1.9.5
|
||||
org.mortbay.jetty:jetty-util:6.1.26
|
||||
org.mortbay.jetty:jetty:6.1.26
|
||||
org.objenesis:objenesis:1.2
|
||||
org.ow2.asm:asm-analysis:6.0
|
||||
org.ow2.asm:asm-analysis:7.0
|
||||
org.ow2.asm:asm-commons:6.0
|
||||
org.ow2.asm:asm-tree:6.0
|
||||
org.ow2.asm:asm-util:6.0
|
||||
org.ow2.asm:asm:6.0
|
||||
org.ow2.asm:asm-tree:7.0
|
||||
org.ow2.asm:asm-util:7.0
|
||||
org.ow2.asm:asm:7.0
|
||||
org.postgresql:postgresql:42.2.6
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
|
||||
@@ -11,6 +11,14 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
|
||||
com.fasterxml.jackson.core:jackson-core:2.10.2
|
||||
com.fasterxml.jackson.core:jackson-databind:2.9.10
|
||||
com.fasterxml:classmate:1.3.4
|
||||
com.github.jnr:jffi:1.2.17
|
||||
com.github.jnr:jnr-a64asm:1.0.0
|
||||
com.github.jnr:jnr-constants:0.9.11
|
||||
com.github.jnr:jnr-enxio:0.19
|
||||
com.github.jnr:jnr-ffi:2.1.9
|
||||
com.github.jnr:jnr-posix:3.0.47
|
||||
com.github.jnr:jnr-unixsocket:0.21
|
||||
com.github.jnr:jnr-x86asm:1.0.2
|
||||
com.google.api-client:google-api-client-appengine:1.30.8
|
||||
com.google.api-client:google-api-client-jackson2:1.27.0
|
||||
com.google.api-client:google-api-client-java6:1.27.0
|
||||
@@ -51,6 +59,7 @@ com.google.apis:google-api-services-groupssettings:v1-rev60-1.22.0
|
||||
com.google.apis:google-api-services-monitoring:v3-rev426-1.23.0
|
||||
com.google.apis:google-api-services-pubsub:v1-rev20181105-1.27.0
|
||||
com.google.apis:google-api-services-sheets:v4-rev483-1.22.0
|
||||
com.google.apis:google-api-services-sqladmin:v1beta4-rev56-1.23.0
|
||||
com.google.apis:google-api-services-storage:v1-rev20181109-1.27.0
|
||||
com.google.appengine.tools:appengine-gcs-client:0.6
|
||||
com.google.appengine.tools:appengine-mapreduce:0.9
|
||||
@@ -66,6 +75,7 @@ com.google.cloud.bigdataoss:gcsio:1.9.16
|
||||
com.google.cloud.bigdataoss:util:1.9.16
|
||||
com.google.cloud.bigtable:bigtable-client-core:1.8.0
|
||||
com.google.cloud.datastore:datastore-v1-proto-client:1.6.0
|
||||
com.google.cloud.sql:jdbc-socket-factory-core:1.0.12
|
||||
com.google.cloud:google-cloud-bigquerystorage:0.79.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable-admin:0.73.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable:0.73.0-alpha
|
||||
@@ -213,11 +223,11 @@ org.mockito:mockito-core:1.9.5
|
||||
org.mortbay.jetty:jetty-util:6.1.26
|
||||
org.mortbay.jetty:jetty:6.1.26
|
||||
org.objenesis:objenesis:1.2
|
||||
org.ow2.asm:asm-analysis:6.0
|
||||
org.ow2.asm:asm-analysis:7.0
|
||||
org.ow2.asm:asm-commons:6.0
|
||||
org.ow2.asm:asm-tree:6.0
|
||||
org.ow2.asm:asm-util:6.0
|
||||
org.ow2.asm:asm:6.0
|
||||
org.ow2.asm:asm-tree:7.0
|
||||
org.ow2.asm:asm-util:7.0
|
||||
org.ow2.asm:asm:7.0
|
||||
org.postgresql:postgresql:42.2.6
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
|
||||
@@ -232,6 +232,7 @@ org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
org.scijava:native-lib-loader:2.0.2
|
||||
org.slf4j:slf4j-api:1.7.28
|
||||
org.slf4j:slf4j-jdk14:1.7.28
|
||||
org.testcontainers:database-commons:1.12.1
|
||||
org.testcontainers:jdbc:1.12.1
|
||||
org.testcontainers:postgresql:1.12.1
|
||||
|
||||
@@ -11,6 +11,14 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
|
||||
com.fasterxml.jackson.core:jackson-core:2.10.2
|
||||
com.fasterxml.jackson.core:jackson-databind:2.9.10
|
||||
com.fasterxml:classmate:1.3.4
|
||||
com.github.jnr:jffi:1.2.17
|
||||
com.github.jnr:jnr-a64asm:1.0.0
|
||||
com.github.jnr:jnr-constants:0.9.11
|
||||
com.github.jnr:jnr-enxio:0.19
|
||||
com.github.jnr:jnr-ffi:2.1.9
|
||||
com.github.jnr:jnr-posix:3.0.47
|
||||
com.github.jnr:jnr-unixsocket:0.21
|
||||
com.github.jnr:jnr-x86asm:1.0.2
|
||||
com.google.api-client:google-api-client-appengine:1.30.8
|
||||
com.google.api-client:google-api-client-jackson2:1.27.0
|
||||
com.google.api-client:google-api-client-java6:1.27.0
|
||||
@@ -51,6 +59,7 @@ com.google.apis:google-api-services-groupssettings:v1-rev60-1.22.0
|
||||
com.google.apis:google-api-services-monitoring:v3-rev426-1.23.0
|
||||
com.google.apis:google-api-services-pubsub:v1-rev20181105-1.27.0
|
||||
com.google.apis:google-api-services-sheets:v4-rev483-1.22.0
|
||||
com.google.apis:google-api-services-sqladmin:v1beta4-rev56-1.23.0
|
||||
com.google.apis:google-api-services-storage:v1-rev20181109-1.27.0
|
||||
com.google.appengine.tools:appengine-gcs-client:0.6
|
||||
com.google.appengine.tools:appengine-mapreduce:0.9
|
||||
@@ -67,6 +76,7 @@ com.google.cloud.bigdataoss:gcsio:1.9.16
|
||||
com.google.cloud.bigdataoss:util:1.9.16
|
||||
com.google.cloud.bigtable:bigtable-client-core:1.8.0
|
||||
com.google.cloud.datastore:datastore-v1-proto-client:1.6.0
|
||||
com.google.cloud.sql:jdbc-socket-factory-core:1.0.12
|
||||
com.google.cloud:google-cloud-bigquerystorage:0.79.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable-admin:0.73.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable:0.73.0-alpha
|
||||
@@ -233,23 +243,27 @@ org.jboss:jandex:2.0.5.Final
|
||||
org.jetbrains:annotations:17.0.0
|
||||
org.joda:joda-money:1.0.1
|
||||
org.json:json:20160810
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.0
|
||||
org.junit.platform:junit-platform-commons:1.6.0
|
||||
org.junit.platform:junit-platform-engine:1.6.0
|
||||
org.junit.vintage:junit-vintage-engine:5.6.0
|
||||
org.junit:junit-bom:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.1
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.1
|
||||
org.junit.jupiter:junit-jupiter-migrationsupport:5.6.1
|
||||
org.junit.platform:junit-platform-commons:1.6.1
|
||||
org.junit.platform:junit-platform-engine:1.6.1
|
||||
org.junit.platform:junit-platform-launcher:1.6.1
|
||||
org.junit.platform:junit-platform-runner:1.6.1
|
||||
org.junit.platform:junit-platform-suite-api:1.6.1
|
||||
org.junit.vintage:junit-vintage-engine:5.6.1
|
||||
org.junit:junit-bom:5.6.1
|
||||
org.jvnet.staxex:stax-ex:1.8
|
||||
org.mockito:mockito-core:2.25.0
|
||||
org.mortbay.jetty:jetty-util:6.1.26
|
||||
org.mortbay.jetty:jetty:6.1.26
|
||||
org.objenesis:objenesis:2.6
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
org.ow2.asm:asm-analysis:6.0
|
||||
org.ow2.asm:asm-analysis:7.0
|
||||
org.ow2.asm:asm-commons:6.0
|
||||
org.ow2.asm:asm-tree:6.0
|
||||
org.ow2.asm:asm-util:6.0
|
||||
org.ow2.asm:asm:6.0
|
||||
org.ow2.asm:asm-tree:7.0
|
||||
org.ow2.asm:asm-util:7.0
|
||||
org.ow2.asm:asm:7.0
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -11,6 +11,14 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
|
||||
com.fasterxml.jackson.core:jackson-core:2.10.2
|
||||
com.fasterxml.jackson.core:jackson-databind:2.9.10
|
||||
com.fasterxml:classmate:1.3.4
|
||||
com.github.jnr:jffi:1.2.17
|
||||
com.github.jnr:jnr-a64asm:1.0.0
|
||||
com.github.jnr:jnr-constants:0.9.11
|
||||
com.github.jnr:jnr-enxio:0.19
|
||||
com.github.jnr:jnr-ffi:2.1.9
|
||||
com.github.jnr:jnr-posix:3.0.47
|
||||
com.github.jnr:jnr-unixsocket:0.21
|
||||
com.github.jnr:jnr-x86asm:1.0.2
|
||||
com.google.api-client:google-api-client-appengine:1.30.8
|
||||
com.google.api-client:google-api-client-jackson2:1.27.0
|
||||
com.google.api-client:google-api-client-java6:1.27.0
|
||||
@@ -51,6 +59,7 @@ com.google.apis:google-api-services-groupssettings:v1-rev60-1.22.0
|
||||
com.google.apis:google-api-services-monitoring:v3-rev426-1.23.0
|
||||
com.google.apis:google-api-services-pubsub:v1-rev20181105-1.27.0
|
||||
com.google.apis:google-api-services-sheets:v4-rev483-1.22.0
|
||||
com.google.apis:google-api-services-sqladmin:v1beta4-rev56-1.23.0
|
||||
com.google.apis:google-api-services-storage:v1-rev20181109-1.27.0
|
||||
com.google.appengine.tools:appengine-gcs-client:0.6
|
||||
com.google.appengine.tools:appengine-mapreduce:0.9
|
||||
@@ -67,6 +76,7 @@ com.google.cloud.bigdataoss:gcsio:1.9.16
|
||||
com.google.cloud.bigdataoss:util:1.9.16
|
||||
com.google.cloud.bigtable:bigtable-client-core:1.8.0
|
||||
com.google.cloud.datastore:datastore-v1-proto-client:1.6.0
|
||||
com.google.cloud.sql:jdbc-socket-factory-core:1.0.12
|
||||
com.google.cloud:google-cloud-bigquerystorage:0.79.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable-admin:0.73.0-alpha
|
||||
com.google.cloud:google-cloud-bigtable:0.73.0-alpha
|
||||
@@ -231,23 +241,27 @@ org.jboss:jandex:2.0.5.Final
|
||||
org.jetbrains:annotations:17.0.0
|
||||
org.joda:joda-money:1.0.1
|
||||
org.json:json:20160810
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.0
|
||||
org.junit.platform:junit-platform-commons:1.6.0
|
||||
org.junit.platform:junit-platform-engine:1.6.0
|
||||
org.junit.vintage:junit-vintage-engine:5.6.0
|
||||
org.junit:junit-bom:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.1
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.1
|
||||
org.junit.jupiter:junit-jupiter-migrationsupport:5.6.1
|
||||
org.junit.platform:junit-platform-commons:1.6.1
|
||||
org.junit.platform:junit-platform-engine:1.6.1
|
||||
org.junit.platform:junit-platform-launcher:1.6.1
|
||||
org.junit.platform:junit-platform-runner:1.6.1
|
||||
org.junit.platform:junit-platform-suite-api:1.6.1
|
||||
org.junit.vintage:junit-vintage-engine:5.6.1
|
||||
org.junit:junit-bom:5.6.1
|
||||
org.jvnet.staxex:stax-ex:1.8
|
||||
org.mockito:mockito-core:2.25.0
|
||||
org.mortbay.jetty:jetty-util:6.1.26
|
||||
org.mortbay.jetty:jetty:6.1.26
|
||||
org.objenesis:objenesis:2.6
|
||||
org.opentest4j:opentest4j:1.2.0
|
||||
org.ow2.asm:asm-analysis:6.0
|
||||
org.ow2.asm:asm-analysis:7.0
|
||||
org.ow2.asm:asm-commons:6.0
|
||||
org.ow2.asm:asm-tree:6.0
|
||||
org.ow2.asm:asm-util:6.0
|
||||
org.ow2.asm:asm:6.0
|
||||
org.ow2.asm:asm-tree:7.0
|
||||
org.ow2.asm:asm-util:7.0
|
||||
org.ow2.asm:asm:7.0
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8
|
||||
org.rnorth.visible-assertions:visible-assertions:2.1.2
|
||||
org.rnorth:tcp-unix-socket-proxy:1.0.2
|
||||
|
||||
@@ -246,12 +246,16 @@ org.jboss:jandex:2.0.5.Final
|
||||
org.jetbrains:annotations:17.0.0
|
||||
org.joda:joda-money:1.0.1
|
||||
org.json:json:20160810
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.0
|
||||
org.junit.platform:junit-platform-commons:1.6.0
|
||||
org.junit.platform:junit-platform-engine:1.6.0
|
||||
org.junit.vintage:junit-vintage-engine:5.6.0
|
||||
org.junit:junit-bom:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.1
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.1
|
||||
org.junit.jupiter:junit-jupiter-migrationsupport:5.6.1
|
||||
org.junit.platform:junit-platform-commons:1.6.1
|
||||
org.junit.platform:junit-platform-engine:1.6.1
|
||||
org.junit.platform:junit-platform-launcher:1.6.1
|
||||
org.junit.platform:junit-platform-runner:1.6.1
|
||||
org.junit.platform:junit-platform-suite-api:1.6.1
|
||||
org.junit.vintage:junit-vintage-engine:5.6.1
|
||||
org.junit:junit-bom:5.6.1
|
||||
org.jvnet.staxex:stax-ex:1.8
|
||||
org.mockito:mockito-core:2.25.0
|
||||
org.mortbay.jetty:jetty-util:6.1.26
|
||||
|
||||
@@ -246,12 +246,16 @@ org.jboss:jandex:2.0.5.Final
|
||||
org.jetbrains:annotations:17.0.0
|
||||
org.joda:joda-money:1.0.1
|
||||
org.json:json:20160810
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.0
|
||||
org.junit.platform:junit-platform-commons:1.6.0
|
||||
org.junit.platform:junit-platform-engine:1.6.0
|
||||
org.junit.vintage:junit-vintage-engine:5.6.0
|
||||
org.junit:junit-bom:5.6.0
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.1
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.1
|
||||
org.junit.jupiter:junit-jupiter-migrationsupport:5.6.1
|
||||
org.junit.platform:junit-platform-commons:1.6.1
|
||||
org.junit.platform:junit-platform-engine:1.6.1
|
||||
org.junit.platform:junit-platform-launcher:1.6.1
|
||||
org.junit.platform:junit-platform-runner:1.6.1
|
||||
org.junit.platform:junit-platform-suite-api:1.6.1
|
||||
org.junit.vintage:junit-vintage-engine:5.6.1
|
||||
org.junit:junit-bom:5.6.1
|
||||
org.jvnet.staxex:stax-ex:1.8
|
||||
org.mockito:mockito-core:2.25.0
|
||||
org.mortbay.jetty:jetty-util:6.1.26
|
||||
@@ -279,6 +283,7 @@ org.seleniumhq.selenium:selenium-remote-driver:3.141.59
|
||||
org.seleniumhq.selenium:selenium-safari-driver:3.141.59
|
||||
org.seleniumhq.selenium:selenium-support:3.141.59
|
||||
org.slf4j:slf4j-api:1.7.28
|
||||
org.slf4j:slf4j-jdk14:1.7.28
|
||||
org.testcontainers:database-commons:1.12.1
|
||||
org.testcontainers:jdbc:1.12.1
|
||||
org.testcontainers:postgresql:1.12.1
|
||||
|
||||
@@ -30,6 +30,8 @@ import google.registry.model.EppResource;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.schema.domain.RegistryLock;
|
||||
import google.registry.util.AppEngineServiceUtils;
|
||||
import google.registry.util.Retrier;
|
||||
import javax.inject.Inject;
|
||||
@@ -148,15 +150,35 @@ public final class AsyncTaskEnqueuer {
|
||||
|
||||
/** Enqueues a task to asynchronously refresh DNS for a renamed host. */
|
||||
public void enqueueAsyncDnsRefresh(HostResource host, DateTime now) {
|
||||
Key<HostResource> hostKey = Key.create(host);
|
||||
VKey<HostResource> hostKey = host.createKey();
|
||||
logger.atInfo().log("Enqueuing async DNS refresh for renamed host %s.", hostKey);
|
||||
addTaskToQueueWithRetry(
|
||||
asyncDnsRefreshPullQueue,
|
||||
TaskOptions.Builder.withMethod(Method.PULL)
|
||||
.param(PARAM_HOST_KEY, hostKey.getString())
|
||||
.param(PARAM_HOST_KEY, hostKey.getOfyKey().getString())
|
||||
.param(PARAM_REQUESTED_TIME, now.toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues a task to asynchronously re-lock a registry-locked domain after it was unlocked.
|
||||
*
|
||||
* <p>Note: the relockDuration must be present on the lock object.
|
||||
*/
|
||||
public void enqueueDomainRelock(RegistryLock lock) {
|
||||
checkArgument(
|
||||
lock.getRelockDuration().isPresent(),
|
||||
"Lock with ID %s not configured for relock",
|
||||
lock.getRevisionId());
|
||||
addTaskToQueueWithRetry(
|
||||
asyncActionsPushQueue,
|
||||
TaskOptions.Builder.withUrl(RelockDomainAction.PATH)
|
||||
.method(Method.POST)
|
||||
.param(
|
||||
RelockDomainAction.OLD_UNLOCK_REVISION_ID_PARAM,
|
||||
String.valueOf(lock.getRevisionId()))
|
||||
.countdownMillis(lock.getRelockDuration().get().getMillis()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a task to a queue with retrying, to avoid aborting the entire flow over a transient issue
|
||||
* enqueuing a task.
|
||||
|
||||
@@ -21,6 +21,7 @@ import static google.registry.batch.AsyncTaskEnqueuer.PARAM_RESOURCE_KEY;
|
||||
import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS;
|
||||
import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_DELETE;
|
||||
import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_HOST_RENAME;
|
||||
import static google.registry.request.RequestParameters.extractLongParameter;
|
||||
import static google.registry.request.RequestParameters.extractOptionalBooleanParameter;
|
||||
import static google.registry.request.RequestParameters.extractOptionalIntParameter;
|
||||
import static google.registry.request.RequestParameters.extractOptionalParameter;
|
||||
@@ -40,9 +41,7 @@ import javax.inject.Named;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Dagger module for injecting common settings for batch actions.
|
||||
*/
|
||||
/** Dagger module for injecting common settings for batch actions. */
|
||||
@Module
|
||||
public class BatchModule {
|
||||
|
||||
@@ -94,6 +93,12 @@ public class BatchModule {
|
||||
return extractSetOfDatetimeParameters(req, PARAM_RESAVE_TIMES);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("oldUnlockRevisionId")
|
||||
static long provideOldUnlockRevisionId(HttpServletRequest req) {
|
||||
return extractLongParameter(req, "oldUnlockRevisionId");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named(QUEUE_ASYNC_ACTIONS)
|
||||
static Queue provideAsyncActionsPushQueue() {
|
||||
|
||||
@@ -85,6 +85,7 @@ import google.registry.model.poll.PendingActionNotificationResponse.HostPendingA
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.server.Lock;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
@@ -284,7 +285,9 @@ public class DeleteContactsAndHostsAction implements Runnable {
|
||||
if (resourceKey.getKind().equals(KIND_CONTACT)) {
|
||||
return domain.getReferencedContacts().contains(resourceKey);
|
||||
} else if (resourceKey.getKind().equals(KIND_HOST)) {
|
||||
return domain.getNameservers().contains(resourceKey);
|
||||
return domain
|
||||
.getNameservers()
|
||||
.contains(VKey.createOfy(HostResource.class, (Key<HostResource>) resourceKey));
|
||||
} else {
|
||||
throw new IllegalStateException("EPP resource key of unknown type: " + resourceKey);
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import google.registry.mapreduce.inputs.NullInput;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.server.Lock;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
@@ -206,7 +207,9 @@ public class RefreshDnsOnHostRenameAction implements Runnable {
|
||||
Key<HostResource> referencingHostKey = null;
|
||||
for (DnsRefreshRequest request : refreshRequests) {
|
||||
if (isActive(domain, request.lastUpdateTime())
|
||||
&& domain.getNameservers().contains(request.hostKey())) {
|
||||
&& domain
|
||||
.getNameservers()
|
||||
.contains(VKey.createOfy(HostResource.class, request.hostKey()))) {
|
||||
referencingHostKey = request.hostKey();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.batch;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.registry.RegistryLockDao;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.schema.domain.RegistryLock;
|
||||
import google.registry.tools.DomainLockUtils;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Task that relocks a previously-Registry-Locked domain after some predetermined period of time.
|
||||
*/
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
path = RelockDomainAction.PATH,
|
||||
method = POST,
|
||||
automaticallyPrintOk = true,
|
||||
auth = Auth.AUTH_INTERNAL_OR_ADMIN)
|
||||
public class RelockDomainAction implements Runnable {
|
||||
|
||||
public static final String PATH = "/_dr/task/relockDomain";
|
||||
public static final String OLD_UNLOCK_REVISION_ID_PARAM = "oldUnlockRevisionId";
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private final long oldUnlockRevisionId;
|
||||
private final DomainLockUtils domainLockUtils;
|
||||
private final Response response;
|
||||
|
||||
@Inject
|
||||
public RelockDomainAction(
|
||||
@Parameter(OLD_UNLOCK_REVISION_ID_PARAM) long oldUnlockRevisionId,
|
||||
DomainLockUtils domainLockUtils,
|
||||
Response response) {
|
||||
this.oldUnlockRevisionId = oldUnlockRevisionId;
|
||||
this.domainLockUtils = domainLockUtils;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
jpaTm().transact(this::relockDomain);
|
||||
}
|
||||
|
||||
private void relockDomain() {
|
||||
RegistryLock oldLock;
|
||||
try {
|
||||
oldLock =
|
||||
RegistryLockDao.getByRevisionId(oldUnlockRevisionId)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format("Unknown revision ID %d", oldUnlockRevisionId)));
|
||||
DomainBase domain =
|
||||
ofy()
|
||||
.load()
|
||||
.type(DomainBase.class)
|
||||
.id(oldLock.getRepoId())
|
||||
.now()
|
||||
.cloneProjectedAtTime(jpaTm().getTransactionTime());
|
||||
|
||||
if (domain.getStatusValues().containsAll(REGISTRY_LOCK_STATUSES)
|
||||
|| oldLock.getRelock() != null) {
|
||||
// The domain was manually locked, so we shouldn't worry about relocking
|
||||
String message =
|
||||
String.format(
|
||||
"Domain %s is already manually relocked, skipping automated relock.",
|
||||
domain.getFullyQualifiedDomainName());
|
||||
logger.atInfo().log(message);
|
||||
// SC_NO_CONTENT (204) skips retry -- see the comment below
|
||||
response.setStatus(SC_NO_CONTENT);
|
||||
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
||||
response.setPayload(message);
|
||||
return;
|
||||
}
|
||||
verifyDomainAndLockState(oldLock, domain);
|
||||
} catch (Throwable t) {
|
||||
/* If there's a bad verification code or the domain is in a bad state, we won't want to retry.
|
||||
* AppEngine will retry on non-2xx error codes, so we return SC_NO_CONTENT (204) to avoid it.
|
||||
*
|
||||
* See https://cloud.google.com/appengine/docs/standard/java/taskqueue/push/retrying-tasks
|
||||
* for more details on retry behavior. */
|
||||
logger.atWarning().withCause(t).log(
|
||||
"Exception when attempting to relock domain with old revision ID %d.",
|
||||
oldUnlockRevisionId);
|
||||
response.setStatus(SC_NO_CONTENT);
|
||||
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
||||
response.setPayload(String.format("Relock failed: %s", t.getMessage()));
|
||||
return;
|
||||
}
|
||||
applyRelock(oldLock);
|
||||
}
|
||||
|
||||
private void applyRelock(RegistryLock oldLock) {
|
||||
try {
|
||||
domainLockUtils.administrativelyApplyLock(
|
||||
oldLock.getDomainName(),
|
||||
oldLock.getRegistrarId(),
|
||||
oldLock.getRegistrarPocId(),
|
||||
oldLock.isSuperuser());
|
||||
logger.atInfo().log("Relocked domain %s.", oldLock.getDomainName());
|
||||
response.setStatus(SC_OK);
|
||||
} catch (Throwable t) {
|
||||
// Any errors that occur here are unexpected, so we should retry. Return a non-2xx
|
||||
// error code to get AppEngine to retry
|
||||
logger.atSevere().withCause(t).log(
|
||||
"Exception when attempting to relock domain %s.", oldLock.getDomainName());
|
||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
||||
response.setPayload(String.format("Relock failed: %s", t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyDomainAndLockState(RegistryLock oldLock, DomainBase domain) {
|
||||
// Domain shouldn't be deleted or have a pending transfer/delete
|
||||
String domainName = domain.getFullyQualifiedDomainName();
|
||||
checkArgument(
|
||||
!DateTimeUtils.isAtOrAfter(jpaTm().getTransactionTime(), domain.getDeletionTime()),
|
||||
"Domain %s has been deleted",
|
||||
domainName);
|
||||
ImmutableSet<StatusValue> statusValues = domain.getStatusValues();
|
||||
checkArgument(
|
||||
!statusValues.contains(StatusValue.PENDING_DELETE),
|
||||
"Domain %s has a pending delete",
|
||||
domainName);
|
||||
checkArgument(
|
||||
!statusValues.contains(StatusValue.PENDING_TRANSFER),
|
||||
"Domain %s has a pending transfer",
|
||||
domainName);
|
||||
checkArgument(
|
||||
domain.getCurrentSponsorClientId().equals(oldLock.getRegistrarId()),
|
||||
"Domain %s has been transferred from registrar %s to registrar %s since the unlock",
|
||||
domainName,
|
||||
oldLock.getRegistrarId(),
|
||||
domain.getCurrentSponsorClientId());
|
||||
}
|
||||
}
|
||||
@@ -279,6 +279,18 @@
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/icannReportingUpload]]></url>
|
||||
<description>
|
||||
Checks if the monthly ICANN reports have been successfully uploaded. If they have not, attempts to upload them again.
|
||||
Most of the time, this job should not do anything since the uploads are triggered when the reports are staged.
|
||||
However, in the event that an upload failed for any reason (e.g. ICANN server is down, IP whitelist issues),
|
||||
this cron job will continue to retry uploads daily until they succeed.
|
||||
</description>
|
||||
<schedule>every day 15:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/generateInvoices&runInEmpty]]></url>
|
||||
<description>
|
||||
|
||||
@@ -80,11 +80,9 @@ public final class ResourceFlowUtils {
|
||||
final Function<DomainBase, ImmutableSet<?>> getPotentialReferences) throws EppException {
|
||||
// Enter a transactionless context briefly.
|
||||
EppException failfastException =
|
||||
tm()
|
||||
.doTransactionless(
|
||||
tm().doTransactionless(
|
||||
() -> {
|
||||
final ForeignKeyIndex<R> fki =
|
||||
ForeignKeyIndex.load(resourceClass, targetId, now);
|
||||
final ForeignKeyIndex<R> fki = ForeignKeyIndex.load(resourceClass, targetId, now);
|
||||
if (fki == null) {
|
||||
return new ResourceDoesNotExistException(resourceClass, targetId);
|
||||
}
|
||||
@@ -99,8 +97,7 @@ public final class ResourceFlowUtils {
|
||||
.limit(FAILFAST_CHECK_COUNT)
|
||||
.keys();
|
||||
Predicate<DomainBase> predicate =
|
||||
domain ->
|
||||
getPotentialReferences.apply(domain).contains(fki.getResourceKey());
|
||||
domain -> getPotentialReferences.apply(domain).contains(fki.getResourceKey());
|
||||
return ofy().load().keys(keys).values().stream().anyMatch(predicate)
|
||||
? new ResourceToDeleteIsReferencedException()
|
||||
: null;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.flows.domain;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.flows.FlowUtils.persistEntityChanges;
|
||||
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist;
|
||||
@@ -95,6 +96,7 @@ import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.CreateData.DomainCreateData;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.index.EppResourceIndex;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.model.ofy.ObjectifyService;
|
||||
@@ -108,6 +110,7 @@ import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.tmch.LordnTaskUtils;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
@@ -120,6 +123,8 @@ import org.joda.time.Duration;
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException}
|
||||
@@ -350,7 +355,11 @@ public class DomainCreateFlow implements TransactionalFlow {
|
||||
.setRegistrant(command.getRegistrant())
|
||||
.setAuthInfo(command.getAuthInfo())
|
||||
.setFullyQualifiedDomainName(targetId)
|
||||
.setNameservers(command.getNameservers())
|
||||
.setNameservers(
|
||||
(ImmutableSet<VKey<HostResource>>)
|
||||
command.getNameservers().stream()
|
||||
.map(key -> VKey.createOfy(HostResource.class, key))
|
||||
.collect(toImmutableSet()))
|
||||
.setStatusValues(statuses.build())
|
||||
.setContacts(command.getContacts())
|
||||
.addGracePeriod(GracePeriod.forBillingEvent(GracePeriodStatus.ADD, createBillingEvent))
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package google.registry.flows.domain;
|
||||
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyExistence;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo;
|
||||
@@ -23,6 +22,7 @@ import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.loadForeignKeyedDesignatedContacts;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -102,8 +102,9 @@ public final class DomainInfoFlow implements Flow {
|
||||
flowCustomLogic.afterValidation(
|
||||
AfterValidationParameters.newBuilder().setDomain(domain).build());
|
||||
// Prefetch all referenced resources. Calling values() blocks until loading is done.
|
||||
ofy().load()
|
||||
.values(union(domain.getNameservers(), domain.getReferencedContacts())).values();
|
||||
// We do nameservers separately since they've been converted to VKey.
|
||||
tm().load(domain.getNameservers());
|
||||
ofy().load().values(domain.getReferencedContacts()).values();
|
||||
// Registrars can only see a few fields on unauthorized domains.
|
||||
// This is a policy decision that is left up to us by the rfcs.
|
||||
DomainInfoData.Builder infoBuilder = DomainInfoData.newBuilder()
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.flows.domain;
|
||||
|
||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Sets.symmetricDifference;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.flows.FlowUtils.persistEntityChanges;
|
||||
@@ -71,9 +72,11 @@ import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -243,8 +246,14 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
.setLastEppUpdateClientId(clientId)
|
||||
.addStatusValues(add.getStatusValues())
|
||||
.removeStatusValues(remove.getStatusValues())
|
||||
.addNameservers(add.getNameservers())
|
||||
.removeNameservers(remove.getNameservers())
|
||||
.addNameservers(
|
||||
add.getNameservers().stream()
|
||||
.map(key -> VKey.createOfy(HostResource.class, key))
|
||||
.collect(toImmutableSet()))
|
||||
.removeNameservers(
|
||||
remove.getNameservers().stream()
|
||||
.map(key -> VKey.createOfy(HostResource.class, key))
|
||||
.collect(toImmutableSet()))
|
||||
.addContacts(add.getContacts())
|
||||
.removeContacts(remove.getContacts())
|
||||
.setRegistrant(firstNonNull(change.getRegistrant(), domain.getRegistrant()))
|
||||
|
||||
+34
-12
@@ -17,6 +17,7 @@ package google.registry.flows.domain.token;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
@@ -24,7 +25,7 @@ import com.google.common.net.InternetDomainName;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.AssociationProhibitsOperationException;
|
||||
import google.registry.flows.EppException.ParameterValueSyntaxErrorException;
|
||||
import google.registry.flows.EppException.AuthorizationErrorException;
|
||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import google.registry.model.domain.DomainCommand;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
@@ -40,7 +41,7 @@ import org.joda.time.DateTime;
|
||||
/** Utility functions for dealing with {@link AllocationToken}s in domain flows. */
|
||||
public class AllocationTokenFlowUtils {
|
||||
|
||||
final AllocationTokenCustomLogic tokenCustomLogic;
|
||||
private final AllocationTokenCustomLogic tokenCustomLogic;
|
||||
|
||||
@Inject
|
||||
AllocationTokenFlowUtils(AllocationTokenCustomLogic tokenCustomLogic) {
|
||||
@@ -59,7 +60,8 @@ public class AllocationTokenFlowUtils {
|
||||
DomainCommand.Create command, String token, Registry registry, String clientId, DateTime now)
|
||||
throws EppException {
|
||||
AllocationToken tokenEntity = loadToken(token);
|
||||
validateToken(tokenEntity, clientId, registry.getTldStr(), now);
|
||||
validateToken(
|
||||
InternetDomainName.from(command.getFullyQualifiedDomainName()), tokenEntity, clientId, now);
|
||||
return tokenCustomLogic.validateToken(command, tokenEntity, registry, clientId, now);
|
||||
}
|
||||
|
||||
@@ -88,7 +90,7 @@ public class AllocationTokenFlowUtils {
|
||||
ImmutableMap.Builder<InternetDomainName, String> resultsBuilder = new ImmutableMap.Builder<>();
|
||||
for (InternetDomainName domainName : domainNames) {
|
||||
try {
|
||||
validateToken(tokenEntity, clientId, domainName.parent().toString(), now);
|
||||
validateToken(domainName, tokenEntity, clientId, now);
|
||||
validDomainNames.add(domainName);
|
||||
} catch (EppException e) {
|
||||
resultsBuilder.put(domainName, e.getMessage());
|
||||
@@ -119,14 +121,20 @@ public class AllocationTokenFlowUtils {
|
||||
*
|
||||
* @throws EppException if the token is invalid in any way
|
||||
*/
|
||||
private void validateToken(AllocationToken token, String clientId, String tld, DateTime now)
|
||||
private void validateToken(
|
||||
InternetDomainName domainName, AllocationToken token, String clientId, DateTime now)
|
||||
throws EppException {
|
||||
if (!token.getAllowedClientIds().isEmpty() && !token.getAllowedClientIds().contains(clientId)) {
|
||||
throw new AllocationTokenNotValidForRegistrarException();
|
||||
}
|
||||
if (!token.getAllowedTlds().isEmpty() && !token.getAllowedTlds().contains(tld)) {
|
||||
if (!token.getAllowedTlds().isEmpty()
|
||||
&& !token.getAllowedTlds().contains(domainName.parent().toString())) {
|
||||
throw new AllocationTokenNotValidForTldException();
|
||||
}
|
||||
if (token.getDomainName().isPresent()
|
||||
&& !token.getDomainName().get().equals(domainName.toString())) {
|
||||
throw new AllocationTokenNotValidForDomainException();
|
||||
}
|
||||
// Tokens without status transitions will just have a single-entry NOT_STARTED map, so only
|
||||
// check the status transitions map if it's non-trivial.
|
||||
if (token.getTokenStatusTransitions().size() > 1
|
||||
@@ -137,6 +145,12 @@ public class AllocationTokenFlowUtils {
|
||||
|
||||
/** Loads a given token and validates that it is not redeemed */
|
||||
private AllocationToken loadToken(String token) throws EppException {
|
||||
if (Strings.isNullOrEmpty(token)) {
|
||||
// We load the token directly from the input XML. If it's null or empty we should throw
|
||||
// an InvalidAllocationTokenException before the Datastore load attempt fails.
|
||||
// See https://tools.ietf.org/html/draft-ietf-regext-allocation-token-04#section-2.1
|
||||
throw new InvalidAllocationTokenException();
|
||||
}
|
||||
AllocationToken tokenEntity = ofy().load().key(Key.create(AllocationToken.class, token)).now();
|
||||
if (tokenEntity == null) {
|
||||
throw new InvalidAllocationTokenException();
|
||||
@@ -152,22 +166,30 @@ public class AllocationTokenFlowUtils {
|
||||
/** The allocation token is not currently valid. */
|
||||
public static class AllocationTokenNotInPromotionException
|
||||
extends StatusProhibitsOperationException {
|
||||
public AllocationTokenNotInPromotionException() {
|
||||
AllocationTokenNotInPromotionException() {
|
||||
super("Alloc token not in promo period");
|
||||
}
|
||||
}
|
||||
/** The allocation token is not valid for this TLD. */
|
||||
public static class AllocationTokenNotValidForTldException
|
||||
extends AssociationProhibitsOperationException {
|
||||
public AllocationTokenNotValidForTldException() {
|
||||
AllocationTokenNotValidForTldException() {
|
||||
super("Alloc token invalid for TLD");
|
||||
}
|
||||
}
|
||||
|
||||
/** The allocation token is not valid for this domain. */
|
||||
public static class AllocationTokenNotValidForDomainException
|
||||
extends AssociationProhibitsOperationException {
|
||||
AllocationTokenNotValidForDomainException() {
|
||||
super("Alloc token invalid for domain");
|
||||
}
|
||||
}
|
||||
|
||||
/** The allocation token is not valid for this registrar. */
|
||||
public static class AllocationTokenNotValidForRegistrarException
|
||||
extends AssociationProhibitsOperationException {
|
||||
public AllocationTokenNotValidForRegistrarException() {
|
||||
AllocationTokenNotValidForRegistrarException() {
|
||||
super("Alloc token invalid for client");
|
||||
}
|
||||
}
|
||||
@@ -175,14 +197,14 @@ public class AllocationTokenFlowUtils {
|
||||
/** The allocation token was already redeemed. */
|
||||
public static class AlreadyRedeemedAllocationTokenException
|
||||
extends AssociationProhibitsOperationException {
|
||||
public AlreadyRedeemedAllocationTokenException() {
|
||||
AlreadyRedeemedAllocationTokenException() {
|
||||
super("Alloc token was already redeemed");
|
||||
}
|
||||
}
|
||||
|
||||
/** The allocation token is invalid. */
|
||||
public static class InvalidAllocationTokenException extends ParameterValueSyntaxErrorException {
|
||||
public InvalidAllocationTokenException() {
|
||||
public static class InvalidAllocationTokenException extends AuthorizationErrorException {
|
||||
InvalidAllocationTokenException() {
|
||||
super("The allocation token is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.flows.host;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.failfastForAsyncDelete;
|
||||
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
|
||||
@@ -81,6 +82,17 @@ public final class HostDeleteFlow implements TransactionalFlow {
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject HostDeleteFlow() {}
|
||||
|
||||
/**
|
||||
* Hack to convert DomainBase's nameserver VKey's to Ofy Key's.
|
||||
*
|
||||
* <p>We currently need this because {@code failfastForAsyncDelete()} checks to see if a name is
|
||||
* in the ofy keys and is used for both nameservers and contacts. When we convert contacts to
|
||||
* VKey's, we can remove this and do the conversion in {@code failfastForAsyncDelete()}.
|
||||
*/
|
||||
private static ImmutableSet<Key<HostResource>> getNameserverOfyKeys(DomainBase domain) {
|
||||
return domain.getNameservers().stream().map(key -> key.getOfyKey()).collect(toImmutableSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final EppResponse run() throws EppException {
|
||||
extensionManager.register(MetadataExtension.class);
|
||||
@@ -88,7 +100,7 @@ public final class HostDeleteFlow implements TransactionalFlow {
|
||||
validateClientIsLoggedIn(clientId);
|
||||
DateTime now = tm().getTransactionTime();
|
||||
validateHostName(targetId);
|
||||
failfastForAsyncDelete(targetId, now, HostResource.class, DomainBase::getNameservers);
|
||||
failfastForAsyncDelete(targetId, now, HostResource.class, HostDeleteFlow::getNameserverOfyKeys);
|
||||
HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now);
|
||||
verifyNoDisallowedStatuses(existingHost, DISALLOWED_STATUSES);
|
||||
if (!isSuperuser) {
|
||||
|
||||
@@ -42,8 +42,10 @@ import com.google.common.collect.Ordering;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Ignore;
|
||||
import com.googlecode.objectify.annotation.IgnoreSave;
|
||||
import com.googlecode.objectify.annotation.Index;
|
||||
import com.googlecode.objectify.annotation.OnLoad;
|
||||
import com.googlecode.objectify.condition.IfNull;
|
||||
import google.registry.flows.ResourceFlowUtils;
|
||||
import google.registry.model.EppResource;
|
||||
@@ -63,6 +65,7 @@ import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.CollectionUtils;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
@@ -130,9 +133,16 @@ public class DomainBase extends EppResource
|
||||
@Index
|
||||
String tld;
|
||||
|
||||
/** References to hosts that are the nameservers for the domain. */
|
||||
/**
|
||||
* References to hosts that are the nameservers for the domain.
|
||||
*
|
||||
* <p>This is a legacy field: we have to preserve it because it is still persisted and indexed in
|
||||
* the datastore, but all external references go through nsHostVKeys.
|
||||
*/
|
||||
@Index @ElementCollection @Transient Set<Key<HostResource>> nsHosts;
|
||||
|
||||
@Ignore @Transient Set<VKey<HostResource>> nsHostVKeys;
|
||||
|
||||
/**
|
||||
* The union of the contacts visible via {@link #getContacts} and {@link #getRegistrant}.
|
||||
*
|
||||
@@ -240,6 +250,14 @@ public class DomainBase extends EppResource
|
||||
*/
|
||||
DateTime lastTransferTime;
|
||||
|
||||
@OnLoad
|
||||
void load() {
|
||||
nsHostVKeys =
|
||||
nullToEmptyImmutableCopy(nsHosts).stream()
|
||||
.map(hostKey -> VKey.createOfy(HostResource.class, hostKey))
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
public ImmutableSet<String> getSubordinateHosts() {
|
||||
return nullToEmptyImmutableCopy(subordinateHosts);
|
||||
}
|
||||
@@ -299,8 +317,10 @@ public class DomainBase extends EppResource
|
||||
return idnTableName;
|
||||
}
|
||||
|
||||
public ImmutableSet<Key<HostResource>> getNameservers() {
|
||||
return nullToEmptyImmutableCopy(nsHosts);
|
||||
public ImmutableSet<VKey<HostResource>> getNameservers() {
|
||||
// Since nsHostVKeys gets initialized both from setNameservers() and the OnLoad method, this
|
||||
// should always be valid.
|
||||
return nullToEmptyImmutableCopy(nsHostVKeys);
|
||||
}
|
||||
|
||||
public final String getCurrentSponsorClientId() {
|
||||
@@ -482,7 +502,7 @@ public class DomainBase extends EppResource
|
||||
public ImmutableSortedSet<String> loadNameserverFullyQualifiedHostNames() {
|
||||
return ofy()
|
||||
.load()
|
||||
.keys(getNameservers())
|
||||
.keys(getNameservers().stream().map(VKey::getOfyKey).collect(toImmutableSet()))
|
||||
.values()
|
||||
.stream()
|
||||
.map(HostResource::getFullyQualifiedHostName)
|
||||
@@ -542,6 +562,14 @@ public class DomainBase extends EppResource
|
||||
|
||||
Builder(DomainBase instance) {
|
||||
super(instance);
|
||||
|
||||
// Convert nsHosts to nsHostVKeys.
|
||||
if (instance.nsHosts != null) {
|
||||
instance.nsHostVKeys =
|
||||
instance.nsHosts.stream()
|
||||
.map(key -> VKey.createOfy(HostResource.class, key))
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -557,7 +585,7 @@ public class DomainBase extends EppResource
|
||||
} else { // There are nameservers, so make sure INACTIVE isn't there.
|
||||
removeStatusValue(StatusValue.INACTIVE);
|
||||
}
|
||||
|
||||
|
||||
checkArgumentNotNull(
|
||||
emptyToNull(instance.fullyQualifiedDomainName), "Missing fullyQualifiedDomainName");
|
||||
checkArgument(instance.allContacts.stream().anyMatch(IS_REGISTRANT), "Missing registrant");
|
||||
@@ -591,30 +619,45 @@ public class DomainBase extends EppResource
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public Builder setNameservers(Key<HostResource> nameserver) {
|
||||
getInstance().nsHosts = ImmutableSet.of(nameserver);
|
||||
public Builder setNameservers(VKey<HostResource> nameserver) {
|
||||
Optional<Key<HostResource>> nsKey = nameserver.maybeGetOfyKey();
|
||||
if (nsKey.isPresent()) {
|
||||
getInstance().nsHosts = ImmutableSet.of(nsKey.get());
|
||||
} else {
|
||||
getInstance().nsHosts = null;
|
||||
}
|
||||
getInstance().nsHostVKeys = ImmutableSet.of(nameserver);
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public Builder setNameservers(ImmutableSet<Key<HostResource>> nameservers) {
|
||||
getInstance().nsHosts = forceEmptyToNull(nameservers);
|
||||
public Builder setNameservers(ImmutableSet<VKey<HostResource>> nameservers) {
|
||||
// If we have all of the ofy keys, we can set nsHosts. Otherwise, make it null.
|
||||
if (nameservers != null
|
||||
&& nameservers.stream().allMatch(key -> key.maybeGetOfyKey().isPresent())) {
|
||||
getInstance().nsHosts =
|
||||
nameservers.stream().map(key -> key.getOfyKey()).collect(toImmutableSet());
|
||||
} else {
|
||||
getInstance().nsHosts = null;
|
||||
}
|
||||
|
||||
getInstance().nsHostVKeys = forceEmptyToNull(nameservers);
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public Builder addNameserver(Key<HostResource> nameserver) {
|
||||
public Builder addNameserver(VKey<HostResource> nameserver) {
|
||||
return addNameservers(ImmutableSet.of(nameserver));
|
||||
}
|
||||
|
||||
public Builder addNameservers(ImmutableSet<Key<HostResource>> nameservers) {
|
||||
public Builder addNameservers(ImmutableSet<VKey<HostResource>> nameservers) {
|
||||
return setNameservers(
|
||||
ImmutableSet.copyOf(union(getInstance().getNameservers(), nameservers)));
|
||||
}
|
||||
|
||||
public Builder removeNameserver(Key<HostResource> nameserver) {
|
||||
public Builder removeNameserver(VKey<HostResource> nameserver) {
|
||||
return removeNameservers(ImmutableSet.of(nameserver));
|
||||
}
|
||||
|
||||
public Builder removeNameservers(ImmutableSet<Key<HostResource>> nameservers) {
|
||||
public Builder removeNameservers(ImmutableSet<VKey<HostResource>> nameservers) {
|
||||
return setNameservers(
|
||||
ImmutableSet.copyOf(difference(getInstance().getNameservers(), nameservers)));
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import google.registry.model.annotations.ExternalMessagingName;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -117,6 +118,10 @@ public class HostResource extends EppResource implements ForeignKeyedEppResource
|
||||
return fullyQualifiedHostName;
|
||||
}
|
||||
|
||||
public VKey<HostResource> createKey() {
|
||||
return VKey.createOfy(HostResource.class, Key.create(this));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public HostResource cloneProjectedAtTime(DateTime now) {
|
||||
|
||||
@@ -16,8 +16,15 @@ package google.registry.model.ofy;
|
||||
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.TransactionManager;
|
||||
import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.StreamSupport;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Datastore implementation of {@link TransactionManager}. */
|
||||
@@ -83,4 +90,77 @@ public class DatastoreTransactionManager implements TransactionManager {
|
||||
public DateTime getTransactionTime() {
|
||||
return getOfy().getTransactionTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveNew(Object entity) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAllNew(ImmutableCollection<?> entities) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveNewOrUpdate(Object entity) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveNewOrUpdateAll(ImmutableCollection<?> entities) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Object entity) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAll(ImmutableCollection<?> entities) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkExists(Object entity) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean checkExists(VKey<T> key) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
}
|
||||
|
||||
// TODO: add tests for these methods. They currently have some degree of test coverage because
|
||||
// they are used when retrieving the nameservers which require these, as they are now loaded by
|
||||
// VKey instead of by ofy Key. But ideally, there should be one set of TransactionManager
|
||||
// interface tests that are applied to both the datastore and SQL implementations.
|
||||
@Override
|
||||
public <T> Optional<T> load(VKey<T> key) {
|
||||
return Optional.of(getOfy().load().key(key.getOfyKey()).now());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ImmutableList<T> load(Iterable<VKey<T>> keys) {
|
||||
Iterator<Key<T>> iter =
|
||||
StreamSupport.stream(keys.spliterator(), false).map(key -> key.getOfyKey()).iterator();
|
||||
|
||||
// The lambda argument to keys() effectively converts Iterator -> Iterable.
|
||||
return ImmutableList.copyOf(getOfy().load().keys(() -> iter).values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ImmutableList<T> loadAll(Class<T> clazz) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> int delete(VKey<T> key) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void assertDelete(VKey<T> key) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.googlecode.objectify.impl.translate.opt.joda.MoneyStringTranslatorFac
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.EntityClasses;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.translators.BloomFilterOfStringTranslatorFactory;
|
||||
import google.registry.model.translators.CidrAddressBlockTranslatorFactory;
|
||||
import google.registry.model.translators.CommitLogRevisionsTranslatorFactory;
|
||||
@@ -45,6 +46,7 @@ import google.registry.model.translators.DurationTranslatorFactory;
|
||||
import google.registry.model.translators.InetAddressTranslatorFactory;
|
||||
import google.registry.model.translators.ReadableInstantUtcTranslatorFactory;
|
||||
import google.registry.model.translators.UpdateAutoTimestampTranslatorFactory;
|
||||
import google.registry.model.translators.VKeyTranslatorFactory;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
@@ -117,17 +119,19 @@ public class ObjectifyService {
|
||||
|
||||
/** Register translators that allow less common types to be stored directly in Datastore. */
|
||||
private static void registerTranslators() {
|
||||
for (TranslatorFactory<?> translatorFactory : ImmutableList.of(
|
||||
new BloomFilterOfStringTranslatorFactory(),
|
||||
new CidrAddressBlockTranslatorFactory(),
|
||||
new CommitLogRevisionsTranslatorFactory(),
|
||||
new CreateAutoTimestampTranslatorFactory(),
|
||||
new CurrencyUnitTranslatorFactory(),
|
||||
new DurationTranslatorFactory(),
|
||||
new InetAddressTranslatorFactory(),
|
||||
new MoneyStringTranslatorFactory(),
|
||||
new ReadableInstantUtcTranslatorFactory(),
|
||||
new UpdateAutoTimestampTranslatorFactory())) {
|
||||
for (TranslatorFactory<?> translatorFactory :
|
||||
ImmutableList.of(
|
||||
new BloomFilterOfStringTranslatorFactory(),
|
||||
new CidrAddressBlockTranslatorFactory(),
|
||||
new CommitLogRevisionsTranslatorFactory(),
|
||||
new CreateAutoTimestampTranslatorFactory(),
|
||||
new CurrencyUnitTranslatorFactory(),
|
||||
new DurationTranslatorFactory(),
|
||||
new InetAddressTranslatorFactory(),
|
||||
new MoneyStringTranslatorFactory(),
|
||||
new ReadableInstantUtcTranslatorFactory(),
|
||||
new VKeyTranslatorFactory<HostResource>(HostResource.class),
|
||||
new UpdateAutoTimestampTranslatorFactory())) {
|
||||
factory().getTranslators().add(translatorFactory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,8 +392,6 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||
*/
|
||||
@Nullable
|
||||
@Mapify(CurrencyMapper.class)
|
||||
@org.hibernate.annotations.Type(
|
||||
type = "google.registry.persistence.converter.CurrencyToBillingMapUserType")
|
||||
Map<CurrencyUnit, BillingAccountEntry> billingAccountMap;
|
||||
|
||||
/** A billing account entry for this registrar, consisting of a currency and an account Id. */
|
||||
|
||||
@@ -44,7 +44,9 @@ import google.registry.model.Jsonifiable;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
@@ -112,6 +114,9 @@ public class RegistrarContact extends ImmutableObject implements Jsonifiable {
|
||||
@Column(nullable = false)
|
||||
String emailAddress;
|
||||
|
||||
/** External email address of this contact used for registry lock confirmations. */
|
||||
String registryLockEmailAddress;
|
||||
|
||||
/** The voice number of the contact. */
|
||||
String phoneNumber;
|
||||
|
||||
@@ -212,6 +217,10 @@ public class RegistrarContact extends ImmutableObject implements Jsonifiable {
|
||||
return emailAddress;
|
||||
}
|
||||
|
||||
public Optional<String> getRegistryLockEmailAddress() {
|
||||
return Optional.ofNullable(registryLockEmailAddress);
|
||||
}
|
||||
|
||||
public String getPhoneNumber() {
|
||||
return phoneNumber;
|
||||
}
|
||||
@@ -318,6 +327,7 @@ public class RegistrarContact extends ImmutableObject implements Jsonifiable {
|
||||
return new JsonMapBuilder()
|
||||
.put("name", name)
|
||||
.put("emailAddress", emailAddress)
|
||||
.put("registryLockEmailAddress", registryLockEmailAddress)
|
||||
.put("phoneNumber", phoneNumber)
|
||||
.put("faxNumber", faxNumber)
|
||||
.put("types", getTypes().stream().map(Object::toString).collect(joining(",")))
|
||||
@@ -352,6 +362,14 @@ public class RegistrarContact extends ImmutableObject implements Jsonifiable {
|
||||
public RegistrarContact build() {
|
||||
checkNotNull(getInstance().parent, "Registrar parent cannot be null");
|
||||
checkValidEmail(getInstance().emailAddress);
|
||||
// Check allowedToSetRegistryLockPassword here because if we want to allow the user to set
|
||||
// a registry lock password, we must also set up the correct registry lock email concurrently
|
||||
// or beforehand.
|
||||
if (getInstance().allowedToSetRegistryLockPassword) {
|
||||
checkArgument(
|
||||
!isNullOrEmpty(getInstance().registryLockEmailAddress),
|
||||
"Registry lock email must not be null if allowing registry lock access");
|
||||
}
|
||||
return cloneEmptyToNull(super.build());
|
||||
}
|
||||
|
||||
@@ -365,6 +383,11 @@ public class RegistrarContact extends ImmutableObject implements Jsonifiable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) {
|
||||
getInstance().registryLockEmailAddress = registryLockEmailAddress;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPhoneNumber(String phoneNumber) {
|
||||
getInstance().phoneNumber = phoneNumber;
|
||||
return this;
|
||||
|
||||
@@ -25,90 +25,107 @@ import javax.persistence.EntityManager;
|
||||
/** Data access object for {@link google.registry.schema.domain.RegistryLock}. */
|
||||
public final class RegistryLockDao {
|
||||
|
||||
/**
|
||||
* Returns the most recent version of the {@link RegistryLock} referred to by the verification
|
||||
* code (there may be two instances of the same code in the database--one after lock object
|
||||
* creation and one after verification.
|
||||
*/
|
||||
/** Returns the {@link RegistryLock} referred to by this revision ID, or empty if none exists. */
|
||||
public static Optional<RegistryLock> getByRevisionId(long revisionId) {
|
||||
jpaTm().assertInTransaction();
|
||||
return Optional.ofNullable(jpaTm().getEntityManager().find(RegistryLock.class, revisionId));
|
||||
}
|
||||
|
||||
/** Returns the most recent version of the {@link RegistryLock} referred to by the code. */
|
||||
public static Optional<RegistryLock> getByVerificationCode(String verificationCode) {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
EntityManager em = jpaTm().getEntityManager();
|
||||
Long revisionId =
|
||||
em.createQuery(
|
||||
"SELECT MAX(revisionId) FROM RegistryLock WHERE verificationCode ="
|
||||
+ " :verificationCode",
|
||||
Long.class)
|
||||
.setParameter("verificationCode", verificationCode)
|
||||
.getSingleResult();
|
||||
return Optional.ofNullable(revisionId)
|
||||
.map(revision -> em.find(RegistryLock.class, revision));
|
||||
});
|
||||
jpaTm().assertInTransaction();
|
||||
EntityManager em = jpaTm().getEntityManager();
|
||||
Long revisionId =
|
||||
em.createQuery(
|
||||
"SELECT MAX(revisionId) FROM RegistryLock WHERE verificationCode ="
|
||||
+ " :verificationCode",
|
||||
Long.class)
|
||||
.setParameter("verificationCode", verificationCode)
|
||||
.getSingleResult();
|
||||
return Optional.ofNullable(revisionId).map(revision -> em.find(RegistryLock.class, revision));
|
||||
}
|
||||
|
||||
/** Returns all lock objects that this registrar has created. */
|
||||
public static ImmutableList<RegistryLock> getLockedDomainsByRegistrarId(String registrarId) {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
ImmutableList.copyOf(
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createQuery(
|
||||
"SELECT lock FROM RegistryLock lock WHERE"
|
||||
+ " lock.registrarId = :registrarId "
|
||||
+ "AND lock.lockCompletionTimestamp IS NOT NULL "
|
||||
+ "AND lock.unlockCompletionTimestamp IS NULL",
|
||||
RegistryLock.class)
|
||||
.setParameter("registrarId", registrarId)
|
||||
.getResultList()));
|
||||
/** Returns all lock objects that this registrar has created, including pending locks. */
|
||||
public static ImmutableList<RegistryLock> getLocksByRegistrarId(String registrarId) {
|
||||
jpaTm().assertInTransaction();
|
||||
return ImmutableList.copyOf(
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createQuery(
|
||||
"SELECT lock FROM RegistryLock lock"
|
||||
+ " WHERE lock.registrarId = :registrarId"
|
||||
+ " AND lock.unlockCompletionTimestamp IS NULL"
|
||||
+ " ORDER BY lock.domainName ASC",
|
||||
RegistryLock.class)
|
||||
.setParameter("registrarId", registrarId)
|
||||
.getResultList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most recent lock object for a given domain specified by repo ID, or empty if this
|
||||
* domain hasn't been locked before.
|
||||
* Returns the most recent lock object for a given domain specified by repo ID.
|
||||
*
|
||||
* <p>Returns empty if this domain hasn't been locked before.
|
||||
*/
|
||||
public static Optional<RegistryLock> getMostRecentByRepoId(String repoId) {
|
||||
jpaTm().assertInTransaction();
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createQuery(
|
||||
"SELECT lock FROM RegistryLock lock WHERE lock.repoId = :repoId"
|
||||
+ " ORDER BY lock.revisionId DESC",
|
||||
RegistryLock.class)
|
||||
.setParameter("repoId", repoId)
|
||||
.setMaxResults(1)
|
||||
.getResultStream()
|
||||
.findFirst());
|
||||
.getEntityManager()
|
||||
.createQuery(
|
||||
"SELECT lock FROM RegistryLock lock WHERE lock.repoId = :repoId"
|
||||
+ " ORDER BY lock.revisionId DESC",
|
||||
RegistryLock.class)
|
||||
.setParameter("repoId", repoId)
|
||||
.setMaxResults(1)
|
||||
.getResultStream()
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most recent verified lock object for a given domain specified by repo ID, or empty
|
||||
* if no lock has ever been finalized for this domain. This is different from {@link
|
||||
* #getMostRecentByRepoId(String)} in that it only returns verified locks.
|
||||
* Returns the most recent verified lock object for a given domain specified by repo ID.
|
||||
*
|
||||
* <p>Returns empty if no lock has ever been finalized for this domain. This is different from
|
||||
* {@link #getMostRecentByRepoId(String)} in that it only returns verified locks.
|
||||
*/
|
||||
public static Optional<RegistryLock> getMostRecentVerifiedLockByRepoId(String repoId) {
|
||||
jpaTm().assertInTransaction();
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createQuery(
|
||||
"SELECT lock FROM RegistryLock lock WHERE lock.repoId = :repoId AND"
|
||||
+ " lock.lockCompletionTimestamp IS NOT NULL ORDER BY lock.revisionId"
|
||||
+ " DESC",
|
||||
RegistryLock.class)
|
||||
.setParameter("repoId", repoId)
|
||||
.setMaxResults(1)
|
||||
.getResultStream()
|
||||
.findFirst());
|
||||
.getEntityManager()
|
||||
.createQuery(
|
||||
"SELECT lock FROM RegistryLock lock WHERE lock.repoId = :repoId AND"
|
||||
+ " lock.lockCompletionTimestamp IS NOT NULL AND"
|
||||
+ " lock.unlockCompletionTimestamp IS NULL ORDER BY lock.revisionId"
|
||||
+ " DESC",
|
||||
RegistryLock.class)
|
||||
.setParameter("repoId", repoId)
|
||||
.setMaxResults(1)
|
||||
.getResultStream()
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most recent verified unlock for a given domain specified by repo ID.
|
||||
*
|
||||
* <p>Returns empty if no unlock has ever been finalized for this domain. This is different from
|
||||
* {@link #getMostRecentByRepoId(String)} in that it only returns verified unlocks.
|
||||
*/
|
||||
public static Optional<RegistryLock> getMostRecentVerifiedUnlockByRepoId(String repoId) {
|
||||
jpaTm().assertInTransaction();
|
||||
return jpaTm()
|
||||
.getEntityManager()
|
||||
.createQuery(
|
||||
"SELECT lock FROM RegistryLock lock WHERE lock.repoId = :repoId AND"
|
||||
+ " lock.unlockCompletionTimestamp IS NOT NULL ORDER BY lock.revisionId"
|
||||
+ " DESC",
|
||||
RegistryLock.class)
|
||||
.setParameter("repoId", repoId)
|
||||
.setMaxResults(1)
|
||||
.getResultStream()
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
public static RegistryLock save(RegistryLock registryLock) {
|
||||
jpaTm().assertInTransaction();
|
||||
checkNotNull(registryLock, "Null registry lock cannot be saved");
|
||||
return jpaTm().transact(() -> jpaTm().getEntityManager().merge(registryLock));
|
||||
return jpaTm().getEntityManager().merge(registryLock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.google.common.base.Strings.emptyToNull;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
import google.registry.model.Buildable.GenericBuilder;
|
||||
import google.registry.model.ImmutableObject;
|
||||
@@ -83,6 +84,13 @@ public abstract class DomainLabelEntry<T extends Comparable<?>, D extends Domain
|
||||
"Label '%s' must be in puny-coded, lower-case form",
|
||||
getInstance().label);
|
||||
checkArgumentNotNull(getInstance().getValue(), "Value must be specified");
|
||||
// Verify that the label creates a valid SLD if we add a TLD to the end of it.
|
||||
// We require that the label is not already a full domain name including a dot.
|
||||
// Domain name validation is tricky, so let InternetDomainName handle it for us.
|
||||
checkArgument(
|
||||
InternetDomainName.from(getInstance().label + ".tld").parts().size() == 2,
|
||||
"Label %s must not be a multi-level domain name",
|
||||
getInstance().label);
|
||||
return super.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.model.server;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
|
||||
@@ -28,6 +29,7 @@ import com.googlecode.objectify.annotation.Id;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.annotations.NotBackedUp;
|
||||
import google.registry.model.annotations.NotBackedUp.Reason;
|
||||
import google.registry.schema.server.LockDao;
|
||||
import google.registry.util.RequestStatusChecker;
|
||||
import google.registry.util.RequestStatusCheckerImpl;
|
||||
import java.io.Serializable;
|
||||
@@ -39,8 +41,8 @@ import org.joda.time.Duration;
|
||||
/**
|
||||
* A lock on some shared resource.
|
||||
*
|
||||
* <p>Locks are either specific to a tld or global to the entire system, in which case a tld of
|
||||
* null is used.
|
||||
* <p>Locks are either specific to a tld or global to the entire system, in which case a tld of null
|
||||
* is used.
|
||||
*
|
||||
* <p>This is the "barebone" lock implementation, that requires manual locking and unlocking. For
|
||||
* safe calls that automatically lock and unlock, see LockHandler.
|
||||
@@ -76,6 +78,18 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
/** When the lock can be considered implicitly released. */
|
||||
DateTime expirationTime;
|
||||
|
||||
public String getRequestLogId() {
|
||||
return requestLogId;
|
||||
}
|
||||
|
||||
public DateTime getExpirationTime() {
|
||||
return expirationTime;
|
||||
}
|
||||
|
||||
public DateTime getAcquiredTime() {
|
||||
return acquiredTime;
|
||||
}
|
||||
|
||||
/** When was the lock acquired. Used for logging. */
|
||||
DateTime acquiredTime;
|
||||
|
||||
@@ -87,10 +101,10 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
String tld;
|
||||
|
||||
/**
|
||||
* Create a new {@link Lock} for the given resource name in the specified tld (which can be
|
||||
* null for cross-tld locks).
|
||||
* Create a new {@link Lock} for the given resource name in the specified tld (which can be null
|
||||
* for cross-tld locks).
|
||||
*/
|
||||
private static Lock create(
|
||||
public static Lock create(
|
||||
String resourceName,
|
||||
@Nullable String tld,
|
||||
String requestLogId,
|
||||
@@ -177,13 +191,28 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
// access to resources like GCS that can't be transactionally rolled back. Therefore, the lock
|
||||
// must be definitively acquired before it is used, even when called inside another transaction.
|
||||
AcquireResult acquireResult =
|
||||
tm()
|
||||
.transactNew(
|
||||
tm().transactNew(
|
||||
() -> {
|
||||
DateTime now = tm().getTransactionTime();
|
||||
|
||||
// Checking if an unexpired lock still exists - if so, the lock can't be acquired.
|
||||
Lock lock = ofy().load().type(Lock.class).id(lockId).now();
|
||||
try {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
Optional<google.registry.schema.server.Lock> cloudSqlLockOptional;
|
||||
if (tld == null) {
|
||||
cloudSqlLockOptional = LockDao.load(resourceName);
|
||||
} else {
|
||||
cloudSqlLockOptional = LockDao.load(resourceName, tld);
|
||||
}
|
||||
LockDao.compare(Optional.ofNullable(lock), cloudSqlLockOptional);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log(
|
||||
"Issue loading and comparing lock from Cloud SQL");
|
||||
}
|
||||
if (lock != null) {
|
||||
logger.atInfo().log(
|
||||
"Loaded existing lock: %s for request: %s", lock.lockId, lock.requestLogId);
|
||||
@@ -207,6 +236,36 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
// contention) and
|
||||
// don't need to be backed up.
|
||||
ofy().saveWithoutBackup().entity(newLock);
|
||||
|
||||
// create and save the lock to Cloud SQL
|
||||
try {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
google.registry.schema.server.Lock cloudSqlLock;
|
||||
if (tld == null) {
|
||||
cloudSqlLock =
|
||||
google.registry.schema.server.Lock.createGlobal(
|
||||
resourceName,
|
||||
requestStatusChecker.getLogId(),
|
||||
now,
|
||||
leaseLength);
|
||||
} else {
|
||||
cloudSqlLock =
|
||||
google.registry.schema.server.Lock.create(
|
||||
resourceName,
|
||||
tld,
|
||||
requestStatusChecker.getLogId(),
|
||||
now,
|
||||
leaseLength);
|
||||
}
|
||||
LockDao.save(cloudSqlLock);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log(
|
||||
"Error saving lock to Cloud SQL: %s", newLock);
|
||||
}
|
||||
|
||||
return AcquireResult.create(now, lock, newLock, lockState);
|
||||
});
|
||||
|
||||
@@ -218,19 +277,51 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
/** Release the lock. */
|
||||
public void release() {
|
||||
// Just use the default clock because we aren't actually doing anything that will use the clock.
|
||||
tm()
|
||||
.transact(
|
||||
tm().transact(
|
||||
() -> {
|
||||
// To release a lock, check that no one else has already obtained it and if not
|
||||
// delete it. If the lock in Datastore was different then this lock is gone already;
|
||||
// this can happen if release() is called around the expiration time and the lock
|
||||
// expires underneath us.
|
||||
Lock loadedLock = ofy().load().type(Lock.class).id(lockId).now();
|
||||
try {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
Optional<google.registry.schema.server.Lock> cloudSqlLockOptional;
|
||||
if (tld == null) {
|
||||
cloudSqlLockOptional = LockDao.load(resourceName);
|
||||
} else {
|
||||
cloudSqlLockOptional = LockDao.load(resourceName, tld);
|
||||
}
|
||||
LockDao.compare(Optional.ofNullable(loadedLock), cloudSqlLockOptional);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log(
|
||||
"Issue loading and comparing lock from Cloud SQL");
|
||||
}
|
||||
if (Lock.this.equals(loadedLock)) {
|
||||
// Use noBackupOfy() so that we don't create a commit log entry for deleting the
|
||||
// lock.
|
||||
logger.atInfo().log("Deleting lock: %s", lockId);
|
||||
ofy().deleteWithoutBackup().entity(Lock.this);
|
||||
|
||||
// Remove the lock from Cloud SQL
|
||||
try {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
if (tld == null) {
|
||||
LockDao.delete(resourceName);
|
||||
} else {
|
||||
LockDao.delete(resourceName, tld);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log(
|
||||
"Error deleting lock from Cloud SQL: %s", loadedLock);
|
||||
}
|
||||
|
||||
lockMetrics.recordRelease(
|
||||
resourceName, tld, new Duration(acquiredTime, tm().getTransactionTime()));
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.translators;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
/**
|
||||
* Translator factory for VKey.
|
||||
*
|
||||
* <p>These get translated to a string containing the URL safe encoding of the objectify key
|
||||
* followed by a (url-unsafe) ampersand delimiter and the SQL key.
|
||||
*/
|
||||
public class VKeyTranslatorFactory<T> extends AbstractSimpleTranslatorFactory<VKey, String> {
|
||||
private final Class<T> refClass;
|
||||
|
||||
public VKeyTranslatorFactory(Class<T> refClass) {
|
||||
super(VKey.class);
|
||||
this.refClass = refClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleTranslator<VKey, String> createTranslator() {
|
||||
return new SimpleTranslator<VKey, String>() {
|
||||
@Override
|
||||
public VKey loadValue(String datastoreValue) {
|
||||
int pos = datastoreValue.indexOf('&');
|
||||
Key ofyKey = null;
|
||||
String sqlKey = null;
|
||||
if (pos > 0) {
|
||||
// We have an objectify key.
|
||||
ofyKey = Key.create(datastoreValue.substring(0, pos));
|
||||
}
|
||||
|
||||
if (pos < datastoreValue.length() - 1) {
|
||||
// We have an SQL key.
|
||||
sqlKey = decode(datastoreValue.substring(pos + 1));
|
||||
}
|
||||
|
||||
return VKey.create(refClass, sqlKey, ofyKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String saveValue(VKey key) {
|
||||
return ((key.getOfyKey() == null) ? "" : key.getOfyKey().getString())
|
||||
+ "&"
|
||||
+ ((key.getSqlKey() == null) ? "" : encode(key.getSqlKey().toString()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static String encode(String val) {
|
||||
try {
|
||||
return URLEncoder.encode(val, UTF_8.toString());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String decode(String encoded) {
|
||||
try {
|
||||
return URLDecoder.decode(encoded, UTF_8.toString());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import google.registry.batch.DeleteLoadTestDataAction;
|
||||
import google.registry.batch.DeleteProberDataAction;
|
||||
import google.registry.batch.ExpandRecurringBillingEventsAction;
|
||||
import google.registry.batch.RefreshDnsOnHostRenameAction;
|
||||
import google.registry.batch.RelockDomainAction;
|
||||
import google.registry.batch.ResaveAllEppResourcesAction;
|
||||
import google.registry.batch.ResaveEntityAction;
|
||||
import google.registry.cron.CommitLogFanoutAction;
|
||||
@@ -86,77 +87,126 @@ import google.registry.tmch.TmchSmdrlAction;
|
||||
@RequestScope
|
||||
@Subcomponent(
|
||||
modules = {
|
||||
BackendModule.class,
|
||||
BackupModule.class,
|
||||
BatchModule.class,
|
||||
BillingModule.class,
|
||||
CloudDnsWriterModule.class,
|
||||
CronModule.class,
|
||||
DnsCountQueryCoordinatorModule.class,
|
||||
DnsModule.class,
|
||||
DnsUpdateConfigModule.class,
|
||||
DnsUpdateWriterModule.class,
|
||||
ExportRequestModule.class,
|
||||
IcannReportingModule.class,
|
||||
MapreduceModule.class,
|
||||
RdeModule.class,
|
||||
ReportingModule.class,
|
||||
RequestModule.class,
|
||||
SheetModule.class,
|
||||
Spec11Module.class,
|
||||
TmchModule.class,
|
||||
VoidDnsWriterModule.class,
|
||||
WhiteboxModule.class,
|
||||
BackendModule.class,
|
||||
BackupModule.class,
|
||||
BatchModule.class,
|
||||
BillingModule.class,
|
||||
CloudDnsWriterModule.class,
|
||||
CronModule.class,
|
||||
DnsCountQueryCoordinatorModule.class,
|
||||
DnsModule.class,
|
||||
DnsUpdateConfigModule.class,
|
||||
DnsUpdateWriterModule.class,
|
||||
ExportRequestModule.class,
|
||||
IcannReportingModule.class,
|
||||
MapreduceModule.class,
|
||||
RdeModule.class,
|
||||
ReportingModule.class,
|
||||
RequestModule.class,
|
||||
SheetModule.class,
|
||||
Spec11Module.class,
|
||||
TmchModule.class,
|
||||
VoidDnsWriterModule.class,
|
||||
WhiteboxModule.class,
|
||||
})
|
||||
interface BackendRequestComponent {
|
||||
|
||||
BackupDatastoreAction backupDatastoreAction();
|
||||
|
||||
BigqueryPollJobAction bigqueryPollJobAction();
|
||||
|
||||
BrdaCopyAction brdaCopyAction();
|
||||
|
||||
CheckBackupAction checkBackupAction();
|
||||
|
||||
CommitLogCheckpointAction commitLogCheckpointAction();
|
||||
|
||||
CommitLogFanoutAction commitLogFanoutAction();
|
||||
|
||||
CopyDetailReportsAction copyDetailReportAction();
|
||||
|
||||
DeleteContactsAndHostsAction deleteContactsAndHostsAction();
|
||||
|
||||
DeleteLoadTestDataAction deleteLoadTestDataAction();
|
||||
|
||||
DeleteOldCommitLogsAction deleteOldCommitLogsAction();
|
||||
|
||||
DeleteProberDataAction deleteProberDataAction();
|
||||
|
||||
ExpandRecurringBillingEventsAction expandRecurringBillingEventsAction();
|
||||
|
||||
ExportCommitLogDiffAction exportCommitLogDiffAction();
|
||||
|
||||
ExportDomainListsAction exportDomainListsAction();
|
||||
|
||||
ExportPremiumTermsAction exportPremiumTermsAction();
|
||||
|
||||
ExportReservedTermsAction exportReservedTermsAction();
|
||||
|
||||
GenerateInvoicesAction generateInvoicesAction();
|
||||
|
||||
GenerateSpec11ReportAction generateSpec11ReportAction();
|
||||
|
||||
IcannReportingStagingAction icannReportingStagingAction();
|
||||
|
||||
IcannReportingUploadAction icannReportingUploadAction();
|
||||
|
||||
NordnUploadAction nordnUploadAction();
|
||||
|
||||
NordnVerifyAction nordnVerifyAction();
|
||||
|
||||
PublishDnsUpdatesAction publishDnsUpdatesAction();
|
||||
|
||||
PublishSpec11ReportAction publishSpec11ReportAction();
|
||||
|
||||
ReadDnsQueueAction readDnsQueueAction();
|
||||
|
||||
RdeReportAction rdeReportAction();
|
||||
|
||||
RdeStagingAction rdeStagingAction();
|
||||
|
||||
RdeUploadAction rdeUploadAction();
|
||||
|
||||
RdeReporter rdeReporter();
|
||||
|
||||
RefreshDnsAction refreshDnsAction();
|
||||
|
||||
RefreshDnsOnHostRenameAction refreshDnsOnHostRenameAction();
|
||||
|
||||
RelockDomainAction relockDomainAction();
|
||||
|
||||
ResaveAllEppResourcesAction resaveAllEppResourcesAction();
|
||||
|
||||
ResaveEntityAction resaveEntityAction();
|
||||
|
||||
SyncGroupMembersAction syncGroupMembersAction();
|
||||
|
||||
SyncRegistrarsSheetAction syncRegistrarsSheetAction();
|
||||
|
||||
TldFanoutAction tldFanoutAction();
|
||||
|
||||
TmchCrlAction tmchCrlAction();
|
||||
|
||||
TmchDnlAction tmchDnlAction();
|
||||
|
||||
TmchSmdrlAction tmchSmdrlAction();
|
||||
|
||||
UploadDatastoreBackupAction uploadDatastoreBackupAction();
|
||||
|
||||
UpdateRegistrarRdapBaseUrlsAction updateRegistrarRdapBaseUrlsAction();
|
||||
|
||||
UpdateSnapshotViewAction updateSnapshotViewAction();
|
||||
|
||||
PublishInvoicesAction uploadInvoicesAction();
|
||||
|
||||
@Subcomponent.Builder
|
||||
abstract class Builder implements RequestComponentBuilder<BackendRequestComponent> {
|
||||
@Override public abstract Builder requestModule(RequestModule requestModule);
|
||||
@Override public abstract BackendRequestComponent build();
|
||||
|
||||
@Override
|
||||
public abstract Builder requestModule(RequestModule requestModule);
|
||||
|
||||
@Override
|
||||
public abstract BackendRequestComponent build();
|
||||
}
|
||||
|
||||
@Module(subcomponents = BackendRequestComponent.class)
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.module.frontend;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Subcomponent;
|
||||
import google.registry.batch.BatchModule;
|
||||
import google.registry.dns.DnsModule;
|
||||
import google.registry.flows.EppTlsAction;
|
||||
import google.registry.flows.FlowComponent;
|
||||
@@ -38,6 +39,7 @@ import google.registry.ui.server.registrar.RegistryLockVerifyAction;
|
||||
@RequestScope
|
||||
@Subcomponent(
|
||||
modules = {
|
||||
BatchModule.class,
|
||||
DnsModule.class,
|
||||
EppTlsModule.class,
|
||||
RegistrarConsoleModule.class,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package google.registry.persistence;
|
||||
|
||||
import google.registry.persistence.converter.StringCollectionDescriptor;
|
||||
import google.registry.persistence.converter.StringMapDescriptor;
|
||||
import java.sql.Types;
|
||||
import org.hibernate.boot.model.TypeContributions;
|
||||
import org.hibernate.dialect.PostgreSQL95Dialect;
|
||||
@@ -26,7 +27,7 @@ public class NomulusPostgreSQLDialect extends PostgreSQL95Dialect {
|
||||
registerColumnType(Types.VARCHAR, "text");
|
||||
registerColumnType(Types.TIMESTAMP_WITH_TIMEZONE, "timestamptz");
|
||||
registerColumnType(Types.TIMESTAMP, "timestamptz");
|
||||
registerColumnType(Types.OTHER, "hstore");
|
||||
registerColumnType(StringMapDescriptor.COLUMN_TYPE, StringMapDescriptor.COLUMN_NAME);
|
||||
registerColumnType(
|
||||
StringCollectionDescriptor.COLUMN_TYPE, StringCollectionDescriptor.COLUMN_DDL_NAME);
|
||||
}
|
||||
@@ -37,5 +38,7 @@ public class NomulusPostgreSQLDialect extends PostgreSQL95Dialect {
|
||||
super.contributeTypes(typeContributions, serviceRegistry);
|
||||
typeContributions.contributeJavaTypeDescriptor(StringCollectionDescriptor.getInstance());
|
||||
typeContributions.contributeSqlTypeDescriptor(StringCollectionDescriptor.getInstance());
|
||||
typeContributions.contributeJavaTypeDescriptor(StringMapDescriptor.getInstance());
|
||||
typeContributions.contributeSqlTypeDescriptor(StringMapDescriptor.getInstance());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import google.registry.config.CredentialModule;
|
||||
import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.keyring.kms.KmsModule;
|
||||
import google.registry.persistence.PersistenceModule.AppEngineJpaTm;
|
||||
import google.registry.persistence.PersistenceModule.NomulusToolJpaTm;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import google.registry.util.UtilsModule;
|
||||
import javax.inject.Singleton;
|
||||
@@ -39,7 +38,4 @@ public interface PersistenceComponent {
|
||||
|
||||
@AppEngineJpaTm
|
||||
JpaTransactionManager appEngineJpaTransactionManager();
|
||||
|
||||
@NomulusToolJpaTm
|
||||
JpaTransactionManager nomulusToolJpaTransactionManager();
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static google.registry.config.RegistryConfig.getHibernateHikariMaximumPoo
|
||||
import static google.registry.config.RegistryConfig.getHibernateHikariMinimumIdle;
|
||||
import static google.registry.config.RegistryConfig.getHibernateLogSqlQueries;
|
||||
|
||||
import com.google.api.client.auth.oauth2.Credential;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
@@ -29,8 +30,10 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.keyring.kms.KmsKeyring;
|
||||
import google.registry.persistence.transaction.CloudSqlCredentialSupplier;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import google.registry.persistence.transaction.JpaTransactionManagerImpl;
|
||||
import google.registry.tools.AuthModule.CloudSqlClientCredential;
|
||||
import google.registry.util.Clock;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.util.HashMap;
|
||||
@@ -118,7 +121,9 @@ public class PersistenceModule {
|
||||
@Config("toolsCloudSqlUsername") String username,
|
||||
KmsKeyring kmsKeyring,
|
||||
@PartialCloudSqlConfigs ImmutableMap<String, String> cloudSqlConfigs,
|
||||
@CloudSqlClientCredential Credential credential,
|
||||
Clock clock) {
|
||||
CloudSqlCredentialSupplier.setupCredentialSupplier(credential);
|
||||
HashMap<String, String> overrides = Maps.newHashMap(cloudSqlConfigs);
|
||||
overrides.put(Environment.USER, username);
|
||||
overrides.put(Environment.PASS, kmsKeyring.getToolsCloudSqlPassword());
|
||||
@@ -158,7 +163,7 @@ public class PersistenceModule {
|
||||
/** Dagger qualifier for {@link JpaTransactionManager} used for Nomulus tool. */
|
||||
@Qualifier
|
||||
@Documented
|
||||
@interface NomulusToolJpaTm {}
|
||||
public @interface NomulusToolJpaTm {}
|
||||
|
||||
/** Dagger qualifier for the partial Cloud SQL configs. */
|
||||
@Qualifier
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* VKey is an abstraction that encapsulates the key concept.
|
||||
*
|
||||
* <p>A VKey instance must contain both the JPA primary key for the referenced entity class and the
|
||||
* objectify key for the object.
|
||||
*/
|
||||
public class VKey<T> extends ImmutableObject {
|
||||
|
||||
// The primary key for the referenced entity.
|
||||
private final Object primaryKey;
|
||||
|
||||
// The objectify key for the referenced entity.
|
||||
private final com.googlecode.objectify.Key<T> ofyKey;
|
||||
|
||||
private final Class<? extends T> kind;
|
||||
|
||||
private VKey(Class<? extends T> kind, com.googlecode.objectify.Key<T> ofyKey, Object primaryKey) {
|
||||
this.kind = kind;
|
||||
this.ofyKey = ofyKey;
|
||||
this.primaryKey = primaryKey;
|
||||
}
|
||||
|
||||
public static <T> VKey<T> createSql(Class<? extends T> kind, Object primaryKey) {
|
||||
return new VKey(kind, null, primaryKey);
|
||||
}
|
||||
|
||||
public static <T> VKey<T> createOfy(
|
||||
Class<? extends T> kind, com.googlecode.objectify.Key<T> ofyKey) {
|
||||
return new VKey(kind, ofyKey, null);
|
||||
}
|
||||
|
||||
public static <T> VKey<T> create(
|
||||
Class<? extends T> kind, Object primaryKey, com.googlecode.objectify.Key ofyKey) {
|
||||
return new VKey(kind, ofyKey, primaryKey);
|
||||
}
|
||||
|
||||
public Class<? extends T> getKind() {
|
||||
return this.kind;
|
||||
}
|
||||
|
||||
/** Returns the SQL primary key. */
|
||||
public Object getSqlKey() {
|
||||
checkState(primaryKey != null, "Attempting obtain a null SQL key.");
|
||||
return this.primaryKey;
|
||||
}
|
||||
|
||||
/** Returns the SQL primary key if it exists. */
|
||||
public Optional<Object> maybeGetSqlKey() {
|
||||
return Optional.ofNullable(this.primaryKey);
|
||||
}
|
||||
|
||||
/** Returns the objectify key. */
|
||||
public com.googlecode.objectify.Key<T> getOfyKey() {
|
||||
checkState(ofyKey != null, "Attempting obtain a null Objectify key.");
|
||||
return this.ofyKey;
|
||||
}
|
||||
|
||||
/** Returns the objectify key if it exists. */
|
||||
public Optional<com.googlecode.objectify.Key<T>> maybeGetOfyKey() {
|
||||
return Optional.ofNullable(this.ofyKey);
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import static google.registry.model.registrar.Registrar.BillingAccountEntry;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import java.util.Map;
|
||||
import javax.persistence.Converter;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
/** JPA converter for storing/retrieving {@link Map <CurrencyUnit, BillingAccountEntry>} objects. */
|
||||
@Converter(autoApply = true)
|
||||
public class CurrencyToBillingConverter
|
||||
extends StringMapConverterBase<CurrencyUnit, BillingAccountEntry> {
|
||||
|
||||
@Override
|
||||
Map.Entry<String, String> convertToDatabaseMapEntry(
|
||||
Map.Entry<CurrencyUnit, BillingAccountEntry> entry) {
|
||||
return Maps.immutableEntry(entry.getKey().getCode(), entry.getValue().getAccountId());
|
||||
}
|
||||
|
||||
@Override
|
||||
Map.Entry<CurrencyUnit, BillingAccountEntry> convertToEntityMapEntry(
|
||||
Map.Entry<String, String> entry) {
|
||||
CurrencyUnit currencyUnit = CurrencyUnit.of(entry.getKey());
|
||||
BillingAccountEntry billingAccountEntry =
|
||||
new BillingAccountEntry(currencyUnit, entry.getValue());
|
||||
return Maps.immutableEntry(currencyUnit, billingAccountEntry);
|
||||
}
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
|
||||
import google.registry.model.registrar.Registrar.BillingAccountEntry;
|
||||
import java.util.Map;
|
||||
import org.hibernate.usertype.UserType;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
/**
|
||||
* A custom {@link UserType} for storing/retrieving {@link Map<CurrencyUnit, BillingAccountEntry>}
|
||||
* objects.
|
||||
*/
|
||||
public class CurrencyToBillingMapUserType extends MapUserType {
|
||||
|
||||
@Override
|
||||
public Object toEntityTypeMap(Map<String, String> map) {
|
||||
return map == null
|
||||
? null
|
||||
: map.entrySet().stream()
|
||||
.collect(
|
||||
toImmutableMap(
|
||||
entry -> CurrencyUnit.of(entry.getKey()),
|
||||
entry ->
|
||||
new BillingAccountEntry(
|
||||
CurrencyUnit.of(entry.getKey()), entry.getValue())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> toDbSupportedMap(Object map) {
|
||||
return map == null
|
||||
? null
|
||||
: ((Map<CurrencyUnit, BillingAccountEntry>) map)
|
||||
.entrySet().stream()
|
||||
.collect(
|
||||
toImmutableMap(
|
||||
entry -> entry.getKey().getCode(),
|
||||
entry -> entry.getValue().getAccountId()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/** JPA converter to for storing/retrieving {@link org.joda.time.DateTime} objects. */
|
||||
@Converter(autoApply = true)
|
||||
public class DurationConverter implements AttributeConverter<Duration, Long> {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Long convertToDatabaseColumn(@Nullable Duration duration) {
|
||||
return duration == null ? null : duration.getMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Duration convertToEntityAttribute(@Nullable Long dbData) {
|
||||
return dbData == null ? null : new Duration(dbData);
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.Map;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.usertype.UserType;
|
||||
|
||||
/**
|
||||
* A custom {@link UserType} used to convert a Java {@link Map<String, String>} to/from PostgreSQL
|
||||
* hstore type. Per this <a href="https://www.postgresql.org/docs/current/hstore.html">doc</a>, as
|
||||
* hstore keys and values are simply text strings, the type of key and value in the Java map has to
|
||||
* be {@link String} as well.
|
||||
*/
|
||||
public class MapUserType extends MutableUserType {
|
||||
|
||||
@Override
|
||||
public int[] sqlTypes() {
|
||||
return new int[] {Types.OTHER};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class returnedClass() {
|
||||
return Map.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object nullSafeGet(
|
||||
ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
|
||||
throws HibernateException, SQLException {
|
||||
return toEntityTypeMap((Map<String, String>) rs.getObject(names[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nullSafeSet(
|
||||
PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
|
||||
throws HibernateException, SQLException {
|
||||
st.setObject(index, toDbSupportedMap(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass can override this method to convert the {@link Map<String, String>} to a {@link Map}
|
||||
* of specific type defined in the entity class.
|
||||
*/
|
||||
public Object toEntityTypeMap(Map<String, String> map) {
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass can override this method to convert the {@link Map} of specific type to a {@link
|
||||
* Map<String, String>} that can be stored in the hstore type column.
|
||||
*/
|
||||
public Map<String, String> toDbSupportedMap(Object map) {
|
||||
return (Map<String, String>) map;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.usertype.UserType;
|
||||
|
||||
/**
|
||||
* An abstract class represents a mutable Hibernate user type which implements related methods
|
||||
* defined in {@link UserType}.
|
||||
*/
|
||||
public abstract class MutableUserType implements UserType {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object x, Object y) throws HibernateException {
|
||||
return Objects.equals(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(Object x) throws HibernateException {
|
||||
return x == null ? 0 : x.hashCode();
|
||||
}
|
||||
|
||||
// TODO(b/147489651): Investigate how to properly implement the below methods.
|
||||
@Override
|
||||
public Object deepCopy(Object value) throws HibernateException {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable disassemble(Object value) throws HibernateException {
|
||||
return (Serializable) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object assemble(Serializable cached, Object owner) throws HibernateException {
|
||||
return cached;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object replace(Object original, Object target, Object owner) throws HibernateException {
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
|
||||
import google.registry.persistence.converter.StringMapDescriptor.StringMap;
|
||||
import java.util.Map;
|
||||
import javax.persistence.AttributeConverter;
|
||||
|
||||
/**
|
||||
* Base JPA converter for {@link Map} objects that are stored in a column with data type of hstore
|
||||
* in the database.
|
||||
*/
|
||||
public abstract class StringMapConverterBase<K, V>
|
||||
implements AttributeConverter<Map<K, V>, StringMap> {
|
||||
|
||||
abstract Map.Entry<String, String> convertToDatabaseMapEntry(Map.Entry<K, V> entry);
|
||||
|
||||
abstract Map.Entry<K, V> convertToEntityMapEntry(Map.Entry<String, String> entry);
|
||||
|
||||
@Override
|
||||
public StringMap convertToDatabaseColumn(Map<K, V> attribute) {
|
||||
return attribute == null
|
||||
? null
|
||||
: StringMap.create(
|
||||
attribute.entrySet().stream()
|
||||
.map(this::convertToDatabaseMapEntry)
|
||||
.collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> convertToEntityAttribute(StringMap dbData) {
|
||||
return dbData == null
|
||||
? null
|
||||
: dbData.getMap().entrySet().stream()
|
||||
.map(this::convertToEntityMapEntry)
|
||||
.collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import static google.registry.persistence.converter.StringMapDescriptor.StringMap;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.Map;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.ValueExtractor;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.AbstractTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.spi.JdbcRecommendedSqlTypeMappingContext;
|
||||
import org.hibernate.type.descriptor.sql.BasicBinder;
|
||||
import org.hibernate.type.descriptor.sql.BasicExtractor;
|
||||
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
|
||||
|
||||
/**
|
||||
* The {@link JavaTypeDescriptor} and {@link SqlTypeDescriptor} for {@link StringMap}.
|
||||
*
|
||||
* <p>A {@link StringMap} object is a simple wrapper for a {@link Map <String, String>} which can be
|
||||
* stored in a column with data type of hstore in the database. The {@link JavaTypeDescriptor} and
|
||||
* {@link SqlTypeDescriptor} is used by JPA/Hibernate to map between the map and hstore which is the
|
||||
* actual type that JDBC uses to read from and write to the database.
|
||||
*
|
||||
* @see <a
|
||||
* href="https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#basic-jpa-convert">JPA
|
||||
* 2.1 AttributeConverters</a>
|
||||
* @see <a href="https://www.postgresql.org/docs/current/hstore.html">hstore</a>
|
||||
*/
|
||||
public class StringMapDescriptor extends AbstractTypeDescriptor<StringMap>
|
||||
implements SqlTypeDescriptor {
|
||||
public static final int COLUMN_TYPE = Types.OTHER;
|
||||
public static final String COLUMN_NAME = "hstore";
|
||||
private static final StringMapDescriptor INSTANCE = new StringMapDescriptor();
|
||||
|
||||
protected StringMapDescriptor() {
|
||||
super(StringMap.class);
|
||||
}
|
||||
|
||||
public static StringMapDescriptor getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringMap fromString(String string) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Constructing StringMapDescriptor from string is not allowed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X unwrap(StringMap value, Class<X> type, WrapperOptions options) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (Map.class.isAssignableFrom(type)) {
|
||||
return (X) value.getMap();
|
||||
}
|
||||
throw unknownUnwrap(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> StringMap wrap(X value, WrapperOptions options) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof Map) {
|
||||
return StringMap.create((Map<String, String>) value);
|
||||
}
|
||||
throw unknownWrap(value.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlTypeDescriptor getJdbcRecommendedSqlType(JdbcRecommendedSqlTypeMappingContext context) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSqlType() {
|
||||
return COLUMN_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeRemapped() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {
|
||||
return new BasicBinder<X>(javaTypeDescriptor, this) {
|
||||
@Override
|
||||
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
|
||||
throws SQLException {
|
||||
st.setObject(index, getStringMap(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||
throws SQLException {
|
||||
st.setObject(name, getStringMap(value));
|
||||
}
|
||||
|
||||
private Map<String, String> getStringMap(X value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof StringMap) {
|
||||
return ((StringMap) value).getMap();
|
||||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
String.format(
|
||||
"Binding type %s is not supported by StringMapDescriptor",
|
||||
value.getClass().getName()));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueExtractor<X> getExtractor(JavaTypeDescriptor<X> javaTypeDescriptor) {
|
||||
return new BasicExtractor<X>(javaTypeDescriptor, this) {
|
||||
@Override
|
||||
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
|
||||
return javaTypeDescriptor.wrap(rs.getObject(name), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, int index, WrapperOptions options)
|
||||
throws SQLException {
|
||||
return javaTypeDescriptor.wrap(statement.getObject(index), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
|
||||
throws SQLException {
|
||||
return javaTypeDescriptor.wrap(statement.getObject(name), options);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** A simple wrapper class for {@link Map<String, String>}. */
|
||||
public static class StringMap {
|
||||
private Map<String, String> map;
|
||||
|
||||
private StringMap(Map<String, String> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
/** Constructs an instance of {@link StringMap} from the given map. */
|
||||
public static StringMap create(Map<String, String> map) {
|
||||
return new StringMap(ImmutableMap.copyOf(map));
|
||||
}
|
||||
|
||||
/** Returns the underlying {@link Map<String, String>} object. */
|
||||
public Map<String, String> getMap() {
|
||||
return map;
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.transaction;
|
||||
|
||||
import com.google.api.client.auth.oauth2.Credential;
|
||||
import com.google.cloud.sql.CredentialFactory;
|
||||
|
||||
/** Supplier class to provide {@link Credential} for Cloud SQL library. */
|
||||
public class CloudSqlCredentialSupplier implements CredentialFactory {
|
||||
private static Credential credential;
|
||||
|
||||
/** Initialize the supplier with given credential json and scopes. */
|
||||
public static void setupCredentialSupplier(Credential credential) {
|
||||
System.setProperty(
|
||||
CredentialFactory.CREDENTIAL_FACTORY_PROPERTY, CloudSqlCredentialSupplier.class.getName());
|
||||
CloudSqlCredentialSupplier.credential = credential;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Credential create() {
|
||||
return credential;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import javax.persistence.EntityManager;
|
||||
|
||||
/** Sub-interface of {@link TransactionManager} which defines JPA related methods. */
|
||||
public interface JpaTransactionManager extends TransactionManager {
|
||||
|
||||
/** Returns the {@link EntityManager} for the current request. */
|
||||
EntityManager getEntityManager();
|
||||
}
|
||||
|
||||
+209
@@ -14,13 +14,31 @@
|
||||
|
||||
package google.registry.persistence.transaction;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.Clock;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.StreamSupport;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.EntityTransaction;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.metamodel.EntityType;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Implementation of {@link JpaTransactionManager} for JPA compatible database. */
|
||||
@@ -142,6 +160,197 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
return txnInfo.transactionTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveNew(Object entity) {
|
||||
checkArgumentNotNull(entity, "entity must be specified");
|
||||
assertInTransaction();
|
||||
getEntityManager().persist(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAllNew(ImmutableCollection<?> entities) {
|
||||
checkArgumentNotNull(entities, "entities must be specified");
|
||||
assertInTransaction();
|
||||
entities.forEach(this::saveNew);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveNewOrUpdate(Object entity) {
|
||||
checkArgumentNotNull(entity, "entity must be specified");
|
||||
assertInTransaction();
|
||||
getEntityManager().merge(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveNewOrUpdateAll(ImmutableCollection<?> entities) {
|
||||
checkArgumentNotNull(entities, "entities must be specified");
|
||||
assertInTransaction();
|
||||
entities.forEach(this::saveNewOrUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Object entity) {
|
||||
checkArgumentNotNull(entity, "entity must be specified");
|
||||
assertInTransaction();
|
||||
checkArgument(checkExists(entity), "Given entity does not exist");
|
||||
getEntityManager().merge(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAll(ImmutableCollection<?> entities) {
|
||||
checkArgumentNotNull(entities, "entities must be specified");
|
||||
assertInTransaction();
|
||||
entities.forEach(this::update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean checkExists(VKey<T> key) {
|
||||
checkArgumentNotNull(key, "key must be specified");
|
||||
EntityType<?> entityType = getEntityType(key.getKind());
|
||||
ImmutableSet<EntityId> entityIds = getEntityIdsFromSqlKey(entityType, key.getSqlKey());
|
||||
return checkExists(entityType.getName(), entityIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkExists(Object entity) {
|
||||
checkArgumentNotNull(entity, "entity must be specified");
|
||||
EntityType<?> entityType = getEntityType(entity.getClass());
|
||||
ImmutableSet<EntityId> entityIds = getEntityIdsFromEntity(entityType, entity);
|
||||
return checkExists(entityType.getName(), entityIds);
|
||||
}
|
||||
|
||||
private boolean checkExists(String entityName, ImmutableSet<EntityId> entityIds) {
|
||||
assertInTransaction();
|
||||
TypedQuery<Integer> query =
|
||||
getEntityManager()
|
||||
.createQuery(
|
||||
String.format("SELECT 1 FROM %s WHERE %s", entityName, getAndClause(entityIds)),
|
||||
Integer.class)
|
||||
.setMaxResults(1);
|
||||
entityIds.forEach(entityId -> query.setParameter(entityId.name, entityId.value));
|
||||
return query.getResultList().size() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> load(VKey<T> key) {
|
||||
checkArgumentNotNull(key, "key must be specified");
|
||||
assertInTransaction();
|
||||
return Optional.ofNullable(getEntityManager().find(key.getKind(), key.getSqlKey()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ImmutableList<T> load(Iterable<VKey<T>> keys) {
|
||||
checkArgumentNotNull(keys, "keys must be specified");
|
||||
assertInTransaction();
|
||||
return StreamSupport.stream(keys.spliterator(), false)
|
||||
.map(
|
||||
key -> {
|
||||
T entity = getEntityManager().find(key.getKind(), key.getSqlKey());
|
||||
if (entity == null) {
|
||||
throw new NoSuchElementException(
|
||||
key.getKind().getName() + " with key " + key.getSqlKey() + " not found.");
|
||||
}
|
||||
return entity;
|
||||
})
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ImmutableList<T> loadAll(Class<T> clazz) {
|
||||
checkArgumentNotNull(clazz, "clazz must be specified");
|
||||
assertInTransaction();
|
||||
return ImmutableList.copyOf(
|
||||
getEntityManager()
|
||||
.createQuery(
|
||||
String.format("SELECT entity FROM %s entity", getEntityType(clazz).getName()),
|
||||
clazz)
|
||||
.getResultList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> int delete(VKey<T> key) {
|
||||
checkArgumentNotNull(key, "key must be specified");
|
||||
assertInTransaction();
|
||||
EntityType<?> entityType = getEntityType(key.getKind());
|
||||
ImmutableSet<EntityId> entityIds = getEntityIdsFromSqlKey(entityType, key.getSqlKey());
|
||||
String sql =
|
||||
String.format("DELETE FROM %s WHERE %s", entityType.getName(), getAndClause(entityIds));
|
||||
Query query = getEntityManager().createQuery(sql);
|
||||
entityIds.forEach(entityId -> query.setParameter(entityId.name, entityId.value));
|
||||
return query.executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void assertDelete(VKey<T> key) {
|
||||
if (delete(key) != 1) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Error deleting the entity of the key: %s", key.getSqlKey()));
|
||||
}
|
||||
}
|
||||
|
||||
private <T> EntityType<T> getEntityType(Class<T> clazz) {
|
||||
return emf.getMetamodel().entity(clazz);
|
||||
}
|
||||
|
||||
private static class EntityId {
|
||||
private String name;
|
||||
private Object value;
|
||||
|
||||
private EntityId(String name, Object value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static ImmutableSet<EntityId> getEntityIdsFromEntity(
|
||||
EntityType<?> entityType, Object entity) {
|
||||
if (entityType.hasSingleIdAttribute()) {
|
||||
String idName = entityType.getDeclaredId(entityType.getIdType().getJavaType()).getName();
|
||||
Object idValue = getFieldValue(entity, idName);
|
||||
return ImmutableSet.of(new EntityId(idName, idValue));
|
||||
} else {
|
||||
return getEntityIdsFromIdContainer(entityType, entity);
|
||||
}
|
||||
}
|
||||
|
||||
private static ImmutableSet<EntityId> getEntityIdsFromSqlKey(
|
||||
EntityType<?> entityType, Object sqlKey) {
|
||||
if (entityType.hasSingleIdAttribute()) {
|
||||
String idName = entityType.getDeclaredId(entityType.getIdType().getJavaType()).getName();
|
||||
return ImmutableSet.of(new EntityId(idName, sqlKey));
|
||||
} else {
|
||||
return getEntityIdsFromIdContainer(entityType, sqlKey);
|
||||
}
|
||||
}
|
||||
|
||||
private static ImmutableSet<EntityId> getEntityIdsFromIdContainer(
|
||||
EntityType<?> entityType, Object idContainer) {
|
||||
return entityType.getIdClassAttributes().stream()
|
||||
.map(SingularAttribute::getName)
|
||||
.map(
|
||||
idName -> {
|
||||
Object idValue = getFieldValue(idContainer, idName);
|
||||
return new EntityId(idName, idValue);
|
||||
})
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
private String getAndClause(ImmutableSet<EntityId> entityIds) {
|
||||
return entityIds.stream()
|
||||
.map(entityId -> String.format("%s = :%s", entityId.name, entityId.name))
|
||||
.collect(joining(" AND "));
|
||||
}
|
||||
|
||||
private static Object getFieldValue(Object object, String fieldName) {
|
||||
try {
|
||||
Field field = object.getClass().getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(object);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class TransactionInfo {
|
||||
EntityManager entityManager;
|
||||
boolean inTransaction = false;
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
|
||||
package google.registry.persistence.transaction;
|
||||
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -78,4 +82,47 @@ public interface TransactionManager {
|
||||
|
||||
/** Returns the time associated with the start of this particular transaction attempt. */
|
||||
DateTime getTransactionTime();
|
||||
|
||||
/** Persists a new entity in the database, throws exception if the entity already exists. */
|
||||
void saveNew(Object entity);
|
||||
|
||||
/** Persists all new entities in the database, throws exception if any entity already exists. */
|
||||
void saveAllNew(ImmutableCollection<?> entities);
|
||||
|
||||
/** Persists a new entity or update the existing entity in the database. */
|
||||
void saveNewOrUpdate(Object entity);
|
||||
|
||||
/** Persists all new entities or update the existing entities in the database. */
|
||||
void saveNewOrUpdateAll(ImmutableCollection<?> entities);
|
||||
|
||||
/** Updates an entity in the database, throws exception if the entity does not exist. */
|
||||
void update(Object entity);
|
||||
|
||||
/** Updates all entities in the database, throws exception if any entity does not exist. */
|
||||
void updateAll(ImmutableCollection<?> entities);
|
||||
|
||||
/** Returns whether the given entity with same ID exists. */
|
||||
boolean checkExists(Object entity);
|
||||
|
||||
/** Returns whether the entity of given key exists. */
|
||||
<T> boolean checkExists(VKey<T> key);
|
||||
|
||||
/** Loads the entity by its id, returns empty if the entity doesn't exist. */
|
||||
<T> Optional<T> load(VKey<T> key);
|
||||
|
||||
/**
|
||||
* Leads the set of entities by their key id.
|
||||
*
|
||||
* @throws NoSuchElementException if any of the keys are not found.
|
||||
*/
|
||||
<T> ImmutableList<T> load(Iterable<VKey<T>> keys);
|
||||
|
||||
/** Loads all entities of the given type, returns empty if there is no such entity. */
|
||||
<T> ImmutableList<T> loadAll(Class<T> clazz);
|
||||
|
||||
/** Deletes the entity by its id, returns the number of deleted entity. */
|
||||
<T> int delete(VKey<T> key);
|
||||
|
||||
/** Deletes the entity by its id, throws exception if the entity is not deleted. */
|
||||
<T> void assertDelete(VKey<T> key);
|
||||
}
|
||||
|
||||
+16
-7
@@ -16,26 +16,30 @@ package google.registry.persistence.transaction;
|
||||
|
||||
import com.google.appengine.api.utils.SystemProperty;
|
||||
import com.google.appengine.api.utils.SystemProperty.Environment.Value;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Suppliers;
|
||||
import google.registry.model.ofy.DatastoreTransactionManager;
|
||||
import google.registry.persistence.DaggerPersistenceComponent;
|
||||
import google.registry.tools.RegistryToolEnvironment;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/** Factory class to create {@link TransactionManager} instance. */
|
||||
// TODO: Rename this to PersistenceFactory and move to persistence package.
|
||||
public class TransactionManagerFactory {
|
||||
|
||||
private static final TransactionManager TM = createTransactionManager();
|
||||
@VisibleForTesting static JpaTransactionManager jpaTm = createJpaTransactionManager();
|
||||
|
||||
/** Supplier for jpaTm so that it is initialized only once, upon first usage. */
|
||||
@NonFinalForTesting
|
||||
private static Supplier<JpaTransactionManager> jpaTm =
|
||||
Suppliers.memoize(TransactionManagerFactory::createJpaTransactionManager);
|
||||
|
||||
private TransactionManagerFactory() {}
|
||||
|
||||
private static JpaTransactionManager createJpaTransactionManager() {
|
||||
// If we are running a nomulus command, jpaTm will be injected in RegistryCli.java
|
||||
// by calling setJpaTm().
|
||||
if (isInAppEngine()) {
|
||||
return DaggerPersistenceComponent.create().appEngineJpaTransactionManager();
|
||||
} else if (RegistryToolEnvironment.isInRegistryTool()
|
||||
&& RegistryToolEnvironment.isJpaTmEnabled()) {
|
||||
return DaggerPersistenceComponent.create().nomulusToolJpaTransactionManager();
|
||||
} else {
|
||||
return DummyJpaTransactionManager.create();
|
||||
}
|
||||
@@ -68,6 +72,11 @@ public class TransactionManagerFactory {
|
||||
|
||||
/** Returns {@link JpaTransactionManager} instance. */
|
||||
public static JpaTransactionManager jpaTm() {
|
||||
return jpaTm;
|
||||
return jpaTm.get();
|
||||
}
|
||||
|
||||
/** Sets the return of {@link #jpaTm()} to the given instance of {@link JpaTransactionManager}. */
|
||||
public static void setJpaTm(JpaTransactionManager newJpaTm) {
|
||||
jpaTm = Suppliers.ofInstance(newJpaTm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
|
||||
import static google.registry.model.EppResourceUtils.isLinked;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.rdap.RdapIcannStandardInformation.CONTACT_REDACTED_VALUE;
|
||||
import static google.registry.util.CollectionUtils.union;
|
||||
|
||||
@@ -341,8 +342,8 @@ public class RdapJsonFormatter {
|
||||
|
||||
// Kick off the database loads of the nameservers that we will need, so it can load
|
||||
// asynchronously while we load and process the contacts.
|
||||
Map<Key<HostResource>, HostResource> loadedHosts =
|
||||
ofy().load().keys(domainBase.getNameservers());
|
||||
ImmutableSet<HostResource> loadedHosts =
|
||||
ImmutableSet.copyOf(tm().load(domainBase.getNameservers()));
|
||||
// Load the registrant and other contacts and add them to the data.
|
||||
Map<Key<ContactResource>, ContactResource> loadedContacts =
|
||||
ofy().load().keys(domainBase.getReferencedContacts());
|
||||
@@ -378,8 +379,7 @@ public class RdapJsonFormatter {
|
||||
}
|
||||
// Add the nameservers to the data; the load was kicked off above for efficiency.
|
||||
// RDAP Response Profile 2.9: we MUST have the nameservers
|
||||
for (HostResource hostResource :
|
||||
HOST_RESOURCE_ORDERING.immutableSortedCopy(loadedHosts.values())) {
|
||||
for (HostResource hostResource : HOST_RESOURCE_ORDERING.immutableSortedCopy(loadedHosts)) {
|
||||
builder.nameserversBuilder().add(createRdapNameserver(hostResource, OutputDataType.INTERNAL));
|
||||
}
|
||||
|
||||
|
||||
@@ -193,8 +193,7 @@ public class PublishSpec11ReportAction implements Runnable {
|
||||
// Group by email address then flat-map all of the ThreatMatch objects together
|
||||
return ImmutableMap.copyOf(
|
||||
Maps.transformValues(
|
||||
Multimaps.index(registrarThreatMatches, RegistrarThreatMatches::clientId)
|
||||
.asMap(),
|
||||
Multimaps.index(registrarThreatMatches, RegistrarThreatMatches::clientId).asMap(),
|
||||
registrarThreatMatchesCollection ->
|
||||
registrarThreatMatchesCollection.stream()
|
||||
.flatMap(matches -> matches.threatMatches().stream())
|
||||
|
||||
@@ -44,11 +44,11 @@ public final class RequestParameters {
|
||||
* method to yield the following results:
|
||||
*
|
||||
* <ul>
|
||||
* <li>/foo?bar=hello → hello
|
||||
* <li>/foo?bar=hello&bar=there → hello
|
||||
* <li>/foo?bar= → 400 error (empty)
|
||||
* <li>/foo?bar=&bar=there → 400 error (empty)
|
||||
* <li>/foo → 400 error (absent)
|
||||
* <li>/foo?bar=hello → hello
|
||||
* <li>/foo?bar=hello&bar=there → hello
|
||||
* <li>/foo?bar= → 400 error (empty)
|
||||
* <li>/foo?bar=&bar=there → 400 error (empty)
|
||||
* <li>/foo → 400 error (absent)
|
||||
* </ul>
|
||||
*
|
||||
* @throws BadRequestException if request parameter is absent or empty
|
||||
@@ -88,10 +88,27 @@ public final class RequestParameters {
|
||||
* @throws BadRequestException if request parameter is absent, empty, or not a valid integer
|
||||
*/
|
||||
public static int extractIntParameter(HttpServletRequest req, String name) {
|
||||
String stringParam = req.getParameter(name);
|
||||
try {
|
||||
return Integer.parseInt(nullToEmpty(req.getParameter(name)));
|
||||
return Integer.parseInt(nullToEmpty(stringParam));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new BadRequestException("Expected integer: " + name);
|
||||
throw new BadRequestException(
|
||||
String.format("Expected int for parameter %s but received %s", name, stringParam));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns first GET or POST parameter associated with {@code name} as a long.
|
||||
*
|
||||
* @throws BadRequestException if request parameter is absent, empty, or not a valid long
|
||||
*/
|
||||
public static long extractLongParameter(HttpServletRequest req, String name) {
|
||||
String stringParam = req.getParameter(name);
|
||||
try {
|
||||
return Long.parseLong(nullToEmpty(stringParam));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new BadRequestException(
|
||||
String.format("Expected long for parameter %s but received %s", name, stringParam));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,9 +143,7 @@ public final class RequestParameters {
|
||||
if (parameter == null || parameter.isEmpty()) {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
return Splitter.on(',')
|
||||
.splitToList(parameter)
|
||||
.stream()
|
||||
return Splitter.on(',').splitToList(parameter).stream()
|
||||
.filter(s -> !s.isEmpty())
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
@@ -160,8 +175,8 @@ public final class RequestParameters {
|
||||
* @throws BadRequestException if request parameter named {@code name} is absent, empty, or not
|
||||
* equal to any of the values in {@code enumClass}
|
||||
*/
|
||||
public static <C extends Enum<C>>
|
||||
C extractEnumParameter(HttpServletRequest req, Class<C> enumClass, String name) {
|
||||
public static <C extends Enum<C>> C extractEnumParameter(
|
||||
HttpServletRequest req, Class<C> enumClass, String name) {
|
||||
return getEnumValue(enumClass, extractRequiredParameter(req, name), name);
|
||||
}
|
||||
|
||||
@@ -216,9 +231,9 @@ public final class RequestParameters {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns first request parameter associated with {@code name} parsed as an
|
||||
* <a href="https://goo.gl/pk5Q2k">ISO 8601</a> timestamp, e.g. {@code 1984-12-18TZ},
|
||||
* {@code 2000-01-01T16:20:00Z}.
|
||||
* Returns first request parameter associated with {@code name} parsed as an <a
|
||||
* href="https://goo.gl/pk5Q2k">ISO 8601</a> timestamp, e.g. {@code 1984-12-18TZ}, {@code
|
||||
* 2000-01-01T16:20:00Z}.
|
||||
*
|
||||
* @throws BadRequestException if request parameter named {@code name} is absent, empty, or could
|
||||
* not be parsed as an ISO 8601 timestamp
|
||||
@@ -233,9 +248,9 @@ public final class RequestParameters {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns first request parameter associated with {@code name} parsed as an
|
||||
* <a href="https://goo.gl/pk5Q2k">ISO 8601</a> timestamp, e.g. {@code 1984-12-18TZ},
|
||||
* {@code 2000-01-01T16:20:00Z}.
|
||||
* Returns first request parameter associated with {@code name} parsed as an <a
|
||||
* href="https://goo.gl/pk5Q2k">ISO 8601</a> timestamp, e.g. {@code 1984-12-18TZ}, {@code
|
||||
* 2000-01-01T16:20:00Z}.
|
||||
*
|
||||
* @throws BadRequestException if request parameter is present but not a valid {@link DateTime}.
|
||||
*/
|
||||
@@ -262,8 +277,7 @@ public final class RequestParameters {
|
||||
public static ImmutableSet<DateTime> extractSetOfDatetimeParameters(
|
||||
HttpServletRequest req, String name) {
|
||||
try {
|
||||
return extractSetOfParameters(req, name)
|
||||
.stream()
|
||||
return extractSetOfParameters(req, name).stream()
|
||||
.filter(not(String::isEmpty))
|
||||
.map(DateTime::parse)
|
||||
.collect(toImmutableSet());
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -95,11 +96,14 @@ public class CursorDao {
|
||||
public static void saveCursor(google.registry.model.common.Cursor cursor, String scope) {
|
||||
tm().transact(() -> ofy().save().entity(cursor));
|
||||
CursorType type = cursor.getType();
|
||||
checkArgumentNotNull(scope, "The scope of the cursor cannot be null");
|
||||
Cursor cloudSqlCursor = Cursor.create(type, scope, cursor.getCursorTime());
|
||||
try {
|
||||
Cursor cloudSqlCursor = Cursor.create(type, scope, cursor.getCursorTime());
|
||||
save(cloudSqlCursor);
|
||||
logger.atInfo().log(
|
||||
"Rolled forward CloudSQL cursor for %s to %s.", scope, cursor.getCursorTime());
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log("Error saving cursor to Cloud SQL.");
|
||||
logger.atSevere().withCause(e).log("Error saving cursor to Cloud SQL: %s.", cloudSqlCursor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,18 +23,22 @@ import google.registry.model.Buildable;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.UpdateAutoTimestamp;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* Represents a registry lock/unlock object, meaning that the domain is locked on the registry
|
||||
@@ -124,6 +128,14 @@ public final class RegistryLock extends ImmutableObject implements Buildable {
|
||||
@Column(nullable = false)
|
||||
private boolean isSuperuser;
|
||||
|
||||
/** The lock that undoes this lock, if this lock has been unlocked and the domain locked again. */
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "relockRevisionId", referencedColumnName = "revisionId")
|
||||
private RegistryLock relock;
|
||||
|
||||
/** The duration after which we will re-lock this domain after it is unlocked. */
|
||||
private Duration relockDuration;
|
||||
|
||||
/** Time that this entity was last updated. */
|
||||
private UpdateAutoTimestamp lastUpdateTimestamp;
|
||||
|
||||
@@ -180,22 +192,37 @@ public final class RegistryLock extends ImmutableObject implements Buildable {
|
||||
return revisionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* The lock that undoes this lock, if this lock has been unlocked and the domain locked again.
|
||||
*
|
||||
* <p>Note: this is lazily loaded, so it may not be initialized if referenced outside of the
|
||||
* transaction in which this lock is loaded.
|
||||
*/
|
||||
public RegistryLock getRelock() {
|
||||
return relock;
|
||||
}
|
||||
|
||||
/** The duration after which we will re-lock this domain after it is unlocked. */
|
||||
public Optional<Duration> getRelockDuration() {
|
||||
return Optional.ofNullable(relockDuration);
|
||||
}
|
||||
|
||||
public boolean isLocked() {
|
||||
return lockCompletionTimestamp != null && unlockCompletionTimestamp == null;
|
||||
}
|
||||
|
||||
/** Returns true iff the lock was requested >= 1 hour ago and has not been verified. */
|
||||
public boolean isLockRequestExpired(Clock clock) {
|
||||
public boolean isLockRequestExpired(DateTime now) {
|
||||
return !getLockCompletionTimestamp().isPresent()
|
||||
&& isBeforeOrAt(getLockRequestTimestamp(), clock.nowUtc().minusHours(1));
|
||||
&& isBeforeOrAt(getLockRequestTimestamp(), now.minusHours(1));
|
||||
}
|
||||
|
||||
/** Returns true iff the unlock was requested >= 1 hour ago and has not been verified. */
|
||||
public boolean isUnlockRequestExpired(Clock clock) {
|
||||
public boolean isUnlockRequestExpired(DateTime now) {
|
||||
Optional<DateTime> unlockRequestTimestamp = getUnlockRequestTimestamp();
|
||||
return unlockRequestTimestamp.isPresent()
|
||||
&& !getUnlockCompletionTimestamp().isPresent()
|
||||
&& isBeforeOrAt(unlockRequestTimestamp.get(), clock.nowUtc().minusHours(1));
|
||||
&& isBeforeOrAt(unlockRequestTimestamp.get(), now.minusHours(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -267,5 +294,15 @@ public final class RegistryLock extends ImmutableObject implements Buildable {
|
||||
getInstance().isSuperuser = isSuperuser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRelock(RegistryLock relock) {
|
||||
getInstance().relock = relock;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRelockDuration(@Nullable Duration relockDuration) {
|
||||
getInstance().relockDuration = relockDuration;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.schema.registrar;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Data access object for {@link Registrar}. */
|
||||
public class RegistrarDao {
|
||||
|
||||
private RegistrarDao() {}
|
||||
|
||||
/** Persists a new or updates an existing registrar in Cloud SQL. */
|
||||
public static void saveNew(Registrar registrar) {
|
||||
checkArgumentNotNull(registrar, "registrar must be specified");
|
||||
jpaTm().transact(() -> jpaTm().getEntityManager().persist(registrar));
|
||||
}
|
||||
|
||||
/** Updates an existing registrar in Cloud SQL, throws excpetion if it does not exist. */
|
||||
public static void update(Registrar registrar) {
|
||||
checkArgumentNotNull(registrar, "registrar must be specified");
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
checkArgument(
|
||||
checkExists(registrar.getClientId()),
|
||||
"A registrar of this id does not exist: %s.",
|
||||
registrar.getClientId());
|
||||
jpaTm().getEntityManager().merge(registrar);
|
||||
});
|
||||
}
|
||||
|
||||
/** Returns whether the registrar of the given id exists. */
|
||||
public static boolean checkExists(String clientId) {
|
||||
checkArgumentNotNull(clientId, "clientId must be specified");
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createQuery(
|
||||
"SELECT 1 FROM Registrar WHERE clientIdentifier = :clientIdentifier",
|
||||
Integer.class)
|
||||
.setParameter("clientIdentifier", clientId)
|
||||
.setMaxResults(1)
|
||||
.getResultList()
|
||||
.size()
|
||||
> 0);
|
||||
}
|
||||
|
||||
/** Loads the registrar by its id, returns empty if it doesn't exist. */
|
||||
public static Optional<Registrar> load(String clientId) {
|
||||
checkArgumentNotNull(clientId, "clientId must be specified");
|
||||
return Optional.ofNullable(
|
||||
jpaTm().transact(() -> jpaTm().getEntityManager().find(Registrar.class, clientId)));
|
||||
}
|
||||
}
|
||||
@@ -18,18 +18,22 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import static google.registry.schema.server.Lock.GLOBAL;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.schema.server.Lock.LockId;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Data access object class for {@link Lock}. */
|
||||
public class LockDao {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
/** Saves the {@link Lock} object to Cloud SQL. */
|
||||
public static void saveNew(Lock lock) {
|
||||
public static void save(Lock lock) {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
jpaTm().getEntityManager().persist(lock);
|
||||
jpaTm().getEntityManager().merge(lock);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -51,26 +55,82 @@ public class LockDao {
|
||||
* else empty.
|
||||
*/
|
||||
public static Optional<Lock> load(String resourceName) {
|
||||
checkArgumentNotNull(resourceName, "The resource name of the lock to load cannot be null");
|
||||
return Optional.ofNullable(
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm().getEntityManager().find(Lock.class, new LockId(resourceName, GLOBAL))));
|
||||
return load(resourceName, GLOBAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given {@link Lock} object from Cloud SQL. This method is idempotent and will simply
|
||||
* return if the lock has already been deleted.
|
||||
* Deletes the {@link Lock} object with the given resourceName and tld from Cloud SQL. This method
|
||||
* is idempotent and will simply return if the lock has already been deleted.
|
||||
*/
|
||||
public static void delete(Lock lock) {
|
||||
public static void delete(String resourceName, String tld) {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
Optional<Lock> loadedLock = load(lock.resourceName, lock.tld);
|
||||
Optional<Lock> loadedLock = load(resourceName, tld);
|
||||
if (loadedLock.isPresent()) {
|
||||
jpaTm().getEntityManager().remove(loadedLock.get());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the global {@link Lock} object with the given resourceName from Cloud SQL. This method
|
||||
* is idempotent and will simply return if the lock has already been deleted.
|
||||
*/
|
||||
public static void delete(String resourceName) {
|
||||
delete(resourceName, GLOBAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares a {@link google.registry.model.server.Lock} object with a {@link Lock} object, logging
|
||||
* a warning if there are any differences.
|
||||
*/
|
||||
public static void compare(
|
||||
Optional<google.registry.model.server.Lock> datastoreLockOptional,
|
||||
Optional<Lock> cloudSqlLockOptional) {
|
||||
if (!datastoreLockOptional.isPresent()) {
|
||||
cloudSqlLockOptional.ifPresent(
|
||||
value ->
|
||||
logger.atWarning().log(
|
||||
String.format(
|
||||
"Cloud SQL lock for %s with tld %s should be null",
|
||||
value.resourceName, value.tld)));
|
||||
return;
|
||||
}
|
||||
google.registry.schema.server.Lock cloudSqlLock;
|
||||
google.registry.model.server.Lock datastoreLock = datastoreLockOptional.get();
|
||||
if (cloudSqlLockOptional.isPresent()) {
|
||||
cloudSqlLock = cloudSqlLockOptional.get();
|
||||
if (!datastoreLock.getRequestLogId().equals(cloudSqlLock.requestLogId)) {
|
||||
logger.atWarning().log(
|
||||
String.format(
|
||||
"Datastore lock requestLogId of %s does not equal Cloud SQL lock requestLogId of"
|
||||
+ " %s",
|
||||
datastoreLock.getRequestLogId(), cloudSqlLock.requestLogId));
|
||||
}
|
||||
if (!datastoreLock
|
||||
.getAcquiredTime()
|
||||
.equals(DateTimeUtils.toJodaDateTime(cloudSqlLock.acquiredTime))) {
|
||||
logger.atWarning().log(
|
||||
String.format(
|
||||
"Datastore lock acquiredTime of %s does not equal Cloud SQL lock acquiredTime of"
|
||||
+ " %s",
|
||||
datastoreLock.getAcquiredTime(),
|
||||
DateTimeUtils.toJodaDateTime(cloudSqlLock.acquiredTime)));
|
||||
}
|
||||
if (!datastoreLock
|
||||
.getExpirationTime()
|
||||
.equals(DateTimeUtils.toJodaDateTime(cloudSqlLock.expirationTime))) {
|
||||
logger.atWarning().log(
|
||||
String.format(
|
||||
"Datastore lock expirationTime of %s does not equal Cloud SQL lock expirationTime"
|
||||
+ " of %s",
|
||||
datastoreLock.getExpirationTime(),
|
||||
DateTimeUtils.toJodaDateTime(cloudSqlLock.expirationTime)));
|
||||
}
|
||||
} else {
|
||||
logger.atWarning().log(
|
||||
String.format("Datastore lock: %s was not found in Cloud SQL", datastoreLock));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.schema.tld;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import javax.persistence.Column;
|
||||
@@ -26,7 +27,7 @@ import javax.persistence.Id;
|
||||
* <p>These are not persisted directly, but rather, using {@link PremiumList#getLabelsToPrices()}.
|
||||
*/
|
||||
@Entity
|
||||
public class PremiumEntry implements Serializable {
|
||||
public class PremiumEntry extends ImmutableObject implements Serializable {
|
||||
|
||||
@Id
|
||||
@Column(nullable = false)
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.google.api.client.auth.oauth2.Credential;
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets.Details;
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
|
||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
import com.google.api.client.json.JsonFactory;
|
||||
import com.google.api.client.util.store.AbstractDataStoreFactory;
|
||||
@@ -42,6 +43,7 @@ import google.registry.util.GoogleCredentialsBundle;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -92,6 +94,26 @@ public class AuthModule {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/138195359): Deprecate this credential once Cloud SQL socket library uses the new auth
|
||||
// library.
|
||||
@Provides
|
||||
@CloudSqlClientCredential
|
||||
public static Credential providesLocalCredentialForCloudSqlClient(
|
||||
@LocalCredentialJson String credentialJson,
|
||||
@Config("localCredentialOauthScopes") ImmutableList<String> credentialScopes) {
|
||||
try {
|
||||
GoogleCredential credential =
|
||||
GoogleCredential.fromStream(new ByteArrayInputStream(credentialJson.getBytes(UTF_8)));
|
||||
if (credential.createScopedRequired()) {
|
||||
credential = credential.createScoped(credentialScopes);
|
||||
}
|
||||
return credential;
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(
|
||||
"Error occurred while creating a GoogleCredential for Cloud SQL client", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
public static GoogleAuthorizationCodeFlow provideAuthorizationCodeFlow(
|
||||
JsonFactory jsonFactory,
|
||||
@@ -184,6 +206,11 @@ public class AuthModule {
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private @interface StoredCredential {}
|
||||
|
||||
/** Dagger qualifier for {@link Credential} used by the Cloud SQL client in the nomulus tool. */
|
||||
@Qualifier
|
||||
@Documented
|
||||
public @interface CloudSqlClientCredential {}
|
||||
|
||||
/** Dagger qualifier for the credential qualifier consisting of client and scopes. */
|
||||
@Qualifier
|
||||
@Documented
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
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.Multimap;
|
||||
@@ -39,6 +41,11 @@ final class CheckDomainCommand extends NonMutatingEppToolCommand {
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Parameter(
|
||||
names = {"-t", "--token"},
|
||||
description = "Allocation token to use in the command, if desired")
|
||||
private String allocationToken;
|
||||
|
||||
@Inject
|
||||
@Config("registryAdminClientId")
|
||||
String registryAdminClientId;
|
||||
@@ -53,7 +60,11 @@ final class CheckDomainCommand extends NonMutatingEppToolCommand {
|
||||
Multimap<String, String> domainNameMap = validateAndGroupDomainNamesByTld(mainParameters);
|
||||
for (Collection<String> values : domainNameMap.asMap().values()) {
|
||||
setSoyTemplate(DomainCheckSoyInfo.getInstance(), DomainCheckSoyInfo.DOMAINCHECK);
|
||||
addSoyRecord(clientId, new SoyMapData("domainNames", values));
|
||||
SoyMapData soyMapData = new SoyMapData("domainNames", values);
|
||||
if (!isNullOrEmpty(allocationToken)) {
|
||||
soyMapData.put("allocationToken", allocationToken);
|
||||
}
|
||||
addSoyRecord(clientId, soyMapData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,4 +20,4 @@ package google.registry.tools;
|
||||
* <p>Just implementing this is sufficient to use the remote api; {@link RegistryTool} will install
|
||||
* it as needed.
|
||||
*/
|
||||
interface CommandWithRemoteApi extends Command {}
|
||||
public interface CommandWithRemoteApi extends Command {}
|
||||
|
||||
@@ -18,9 +18,20 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Sets.SetView;
|
||||
import java.io.File;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/** Compare two database backups. */
|
||||
/**
|
||||
* Compares two Datastore backups in V3 format on local file system. This is for use in tests and
|
||||
* experiments with small data sizes.
|
||||
*
|
||||
* <p>This utility only supports the current Datastore backup format (version 3). A backup is a
|
||||
* two-level directory hierarchy with data files in level-db format (output-*) and Datastore
|
||||
* metadata files (*.export_metadata).
|
||||
*/
|
||||
class CompareDbBackups {
|
||||
private static final String DS_V3_BACKUP_FILE_PREFIX = "output-";
|
||||
private static final Predicate<File> DATA_FILE_MATCHER =
|
||||
file -> file.isFile() && file.getName().startsWith(DS_V3_BACKUP_FILE_PREFIX);
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length != 2) {
|
||||
@@ -29,9 +40,13 @@ class CompareDbBackups {
|
||||
}
|
||||
|
||||
ImmutableSet<ComparableEntity> entities1 =
|
||||
new RecordAccumulator().readDirectory(new File(args[0])).getComparableEntitySet();
|
||||
new RecordAccumulator()
|
||||
.readDirectory(new File(args[0]), DATA_FILE_MATCHER)
|
||||
.getComparableEntitySet();
|
||||
ImmutableSet<ComparableEntity> entities2 =
|
||||
new RecordAccumulator().readDirectory(new File(args[1])).getComparableEntitySet();
|
||||
new RecordAccumulator()
|
||||
.readDirectory(new File(args[1]), DATA_FILE_MATCHER)
|
||||
.getComparableEntitySet();
|
||||
|
||||
// Calculate the entities added and removed.
|
||||
SetView<ComparableEntity> added = Sets.difference(entities2, entities1);
|
||||
@@ -54,6 +69,10 @@ class CompareDbBackups {
|
||||
System.out.println(entity);
|
||||
}
|
||||
}
|
||||
|
||||
if (added.isEmpty() && removed.isEmpty()) {
|
||||
System.out.printf("\nBoth sets have the same %d entities.\n", entities1.size());
|
||||
}
|
||||
}
|
||||
|
||||
/** Print out multi-line text in a pretty ASCII header frame. */
|
||||
|
||||
@@ -30,15 +30,18 @@ import org.joda.time.DateTime;
|
||||
@Parameters(separators = " =", commandDescription = "Show count of domains on TLD")
|
||||
final class CountDomainsCommand implements CommandWithRemoteApi {
|
||||
|
||||
@Parameter(description = "TLD(s) to count domains on", required = true)
|
||||
private List<String> mainParameters;
|
||||
@Parameter(
|
||||
names = {"-t", "--tld", "--tlds"},
|
||||
description = "Comma-delimited list of TLD(s) to count domains on",
|
||||
required = true)
|
||||
private List<String> tlds;
|
||||
|
||||
@Inject Clock clock;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
DateTime now = clock.nowUtc();
|
||||
assertTldsExist(mainParameters)
|
||||
assertTldsExist(tlds)
|
||||
.forEach(tld -> System.out.printf("%s,%d\n", tld, getCountForTld(tld, now)));
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||
import static google.registry.model.registrar.Registrar.State.ACTIVE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.tools.RegistryToolEnvironment.PRODUCTION;
|
||||
import static google.registry.tools.RegistryToolEnvironment.SANDBOX;
|
||||
import static google.registry.tools.RegistryToolEnvironment.UNITTEST;
|
||||
@@ -32,7 +33,6 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Streams;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.schema.registrar.RegistrarDao;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -72,7 +72,7 @@ final class CreateRegistrarCommand extends CreateOrUpdateRegistrarCommand
|
||||
|
||||
@Override
|
||||
void saveToCloudSql(Registrar registrar) {
|
||||
RegistrarDao.saveNew(registrar);
|
||||
jpaTm().saveNew(registrar);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -24,6 +24,7 @@ import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STAT
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.batch.AsyncTaskEnqueuer;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
@@ -31,12 +32,13 @@ import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.RegistryLockDao;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.schema.domain.RegistryLock;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.StringGenerator;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* Utility functions for validating and applying {@link RegistryLock}s.
|
||||
@@ -50,19 +52,172 @@ public final class DomainLockUtils {
|
||||
private static final int VERIFICATION_CODE_LENGTH = 32;
|
||||
|
||||
private final StringGenerator stringGenerator;
|
||||
private final AsyncTaskEnqueuer asyncTaskEnqueuer;
|
||||
|
||||
@Inject
|
||||
public DomainLockUtils(@Named("base58StringGenerator") StringGenerator stringGenerator) {
|
||||
public DomainLockUtils(
|
||||
@Named("base58StringGenerator") StringGenerator stringGenerator,
|
||||
AsyncTaskEnqueuer asyncTaskEnqueuer) {
|
||||
this.stringGenerator = stringGenerator;
|
||||
this.asyncTaskEnqueuer = asyncTaskEnqueuer;
|
||||
}
|
||||
|
||||
public RegistryLock createRegistryLockRequest(
|
||||
String domainName,
|
||||
String registrarId,
|
||||
@Nullable String registrarPocId,
|
||||
boolean isAdmin,
|
||||
Clock clock) {
|
||||
DomainBase domainBase = getDomain(domainName, clock);
|
||||
/**
|
||||
* Creates and persists a lock request when requested by a user.
|
||||
*
|
||||
* <p>The lock will not be applied until {@link #verifyAndApplyLock} is called.
|
||||
*/
|
||||
public RegistryLock saveNewRegistryLockRequest(
|
||||
String domainName, String registrarId, @Nullable String registrarPocId, boolean isAdmin) {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
RegistryLockDao.save(
|
||||
createLockBuilder(domainName, registrarId, registrarPocId, isAdmin).build()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and persists an unlock request when requested by a user.
|
||||
*
|
||||
* <p>The unlock will not be applied until {@link #verifyAndApplyUnlock} is called.
|
||||
*/
|
||||
public RegistryLock saveNewRegistryUnlockRequest(
|
||||
String domainName, String registrarId, boolean isAdmin, Optional<Duration> relockDuration) {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
RegistryLockDao.save(
|
||||
createUnlockBuilder(domainName, registrarId, isAdmin, relockDuration).build()));
|
||||
}
|
||||
|
||||
/** Verifies and applies the lock request previously requested by a user. */
|
||||
public RegistryLock verifyAndApplyLock(String verificationCode, boolean isAdmin) {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
DateTime now = jpaTm().getTransactionTime();
|
||||
RegistryLock lock = getByVerificationCode(verificationCode);
|
||||
|
||||
checkArgument(
|
||||
!lock.getLockCompletionTimestamp().isPresent(),
|
||||
"Domain %s is already locked",
|
||||
lock.getDomainName());
|
||||
|
||||
checkArgument(
|
||||
!lock.isLockRequestExpired(now),
|
||||
"The pending lock has expired; please try again");
|
||||
|
||||
checkArgument(
|
||||
!lock.isSuperuser() || isAdmin, "Non-admin user cannot complete admin lock");
|
||||
|
||||
RegistryLock newLock =
|
||||
RegistryLockDao.save(lock.asBuilder().setLockCompletionTimestamp(now).build());
|
||||
setAsRelock(newLock);
|
||||
tm().transact(() -> applyLockStatuses(newLock, now));
|
||||
return newLock;
|
||||
});
|
||||
}
|
||||
|
||||
/** Verifies and applies the unlock request previously requested by a user. */
|
||||
public RegistryLock verifyAndApplyUnlock(String verificationCode, boolean isAdmin) {
|
||||
RegistryLock lock =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
DateTime now = jpaTm().getTransactionTime();
|
||||
RegistryLock previousLock = getByVerificationCode(verificationCode);
|
||||
checkArgument(
|
||||
!previousLock.getUnlockCompletionTimestamp().isPresent(),
|
||||
"Domain %s is already unlocked",
|
||||
previousLock.getDomainName());
|
||||
|
||||
checkArgument(
|
||||
!previousLock.isUnlockRequestExpired(now),
|
||||
"The pending unlock has expired; please try again");
|
||||
|
||||
checkArgument(
|
||||
isAdmin || !previousLock.isSuperuser(),
|
||||
"Non-admin user cannot complete admin unlock");
|
||||
|
||||
RegistryLock newLock =
|
||||
RegistryLockDao.save(
|
||||
previousLock.asBuilder().setUnlockCompletionTimestamp(now).build());
|
||||
tm().transact(() -> removeLockStatuses(newLock, isAdmin, now));
|
||||
return newLock;
|
||||
});
|
||||
// Submit relock outside of the transaction to make sure that it fully succeeded
|
||||
submitRelockIfNecessary(lock);
|
||||
return lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and applies a lock in one step.
|
||||
*
|
||||
* <p>This should only be used for admin actions, e.g. Nomulus tool commands or relocks.
|
||||
* Note: in the case of relocks, isAdmin is determined by the previous lock.
|
||||
*/
|
||||
public RegistryLock administrativelyApplyLock(
|
||||
String domainName, String registrarId, @Nullable String registrarPocId, boolean isAdmin) {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
DateTime now = jpaTm().getTransactionTime();
|
||||
RegistryLock newLock =
|
||||
RegistryLockDao.save(
|
||||
createLockBuilder(domainName, registrarId, registrarPocId, isAdmin)
|
||||
.setLockCompletionTimestamp(now)
|
||||
.build());
|
||||
tm().transact(() -> applyLockStatuses(newLock, now));
|
||||
setAsRelock(newLock);
|
||||
return newLock;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and applies an unlock in one step.
|
||||
*
|
||||
* <p>This should only be used for admin actions, e.g. Nomulus tool commands.
|
||||
*/
|
||||
public RegistryLock administrativelyApplyUnlock(
|
||||
String domainName, String registrarId, boolean isAdmin, Optional<Duration> relockDuration) {
|
||||
RegistryLock lock =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
DateTime now = jpaTm().getTransactionTime();
|
||||
RegistryLock result =
|
||||
RegistryLockDao.save(
|
||||
createUnlockBuilder(domainName, registrarId, isAdmin, relockDuration)
|
||||
.setUnlockCompletionTimestamp(now)
|
||||
.build());
|
||||
tm().transact(() -> removeLockStatuses(result, isAdmin, now));
|
||||
return result;
|
||||
});
|
||||
// Submit relock outside of the transaction to make sure that it fully succeeded
|
||||
submitRelockIfNecessary(lock);
|
||||
return lock;
|
||||
}
|
||||
|
||||
private void submitRelockIfNecessary(RegistryLock lock) {
|
||||
if (lock.getRelockDuration().isPresent()) {
|
||||
asyncTaskEnqueuer.enqueueDomainRelock(lock);
|
||||
}
|
||||
}
|
||||
|
||||
private void setAsRelock(RegistryLock newLock) {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
RegistryLockDao.getMostRecentVerifiedUnlockByRepoId(newLock.getRepoId())
|
||||
.ifPresent(
|
||||
oldLock ->
|
||||
RegistryLockDao.save(oldLock.asBuilder().setRelock(newLock).build())));
|
||||
}
|
||||
|
||||
private RegistryLock.Builder createLockBuilder(
|
||||
String domainName, String registrarId, @Nullable String registrarPocId, boolean isAdmin) {
|
||||
DateTime now = jpaTm().getTransactionTime();
|
||||
DomainBase domainBase = getDomain(domainName, now);
|
||||
verifyDomainNotLocked(domainBase);
|
||||
|
||||
// Multiple pending actions are not allowed
|
||||
@@ -70,26 +225,24 @@ public final class DomainLockUtils {
|
||||
.ifPresent(
|
||||
previousLock ->
|
||||
checkArgument(
|
||||
previousLock.isLockRequestExpired(clock)
|
||||
previousLock.isLockRequestExpired(now)
|
||||
|| previousLock.getUnlockCompletionTimestamp().isPresent(),
|
||||
"A pending or completed lock action already exists for %s",
|
||||
previousLock.getDomainName()));
|
||||
|
||||
RegistryLock lock =
|
||||
new RegistryLock.Builder()
|
||||
.setVerificationCode(stringGenerator.createString(VERIFICATION_CODE_LENGTH))
|
||||
.setDomainName(domainName)
|
||||
.setRepoId(domainBase.getRepoId())
|
||||
.setRegistrarId(registrarId)
|
||||
.setRegistrarPocId(registrarPocId)
|
||||
.isSuperuser(isAdmin)
|
||||
.build();
|
||||
return RegistryLockDao.save(lock);
|
||||
return new RegistryLock.Builder()
|
||||
.setVerificationCode(stringGenerator.createString(VERIFICATION_CODE_LENGTH))
|
||||
.setDomainName(domainName)
|
||||
.setRepoId(domainBase.getRepoId())
|
||||
.setRegistrarId(registrarId)
|
||||
.setRegistrarPocId(registrarPocId)
|
||||
.isSuperuser(isAdmin);
|
||||
}
|
||||
|
||||
public RegistryLock createRegistryUnlockRequest(
|
||||
String domainName, String registrarId, boolean isAdmin, Clock clock) {
|
||||
DomainBase domainBase = getDomain(domainName, clock);
|
||||
private RegistryLock.Builder createUnlockBuilder(
|
||||
String domainName, String registrarId, boolean isAdmin, Optional<Duration> relockDuration) {
|
||||
DateTime now = jpaTm().getTransactionTime();
|
||||
DomainBase domainBase = getDomain(domainName, now);
|
||||
Optional<RegistryLock> lockOptional =
|
||||
RegistryLockDao.getMostRecentVerifiedLockByRepoId(domainBase.getRepoId());
|
||||
|
||||
@@ -105,7 +258,7 @@ public final class DomainLockUtils {
|
||||
new RegistryLock.Builder()
|
||||
.setRepoId(domainBase.getRepoId())
|
||||
.setDomainName(domainName)
|
||||
.setLockCompletionTimestamp(clock.nowUtc())
|
||||
.setLockCompletionTimestamp(now)
|
||||
.setRegistrarId(registrarId));
|
||||
} else {
|
||||
verifyDomainLocked(domainBase);
|
||||
@@ -117,7 +270,7 @@ public final class DomainLockUtils {
|
||||
checkArgument(
|
||||
lock.isLocked(), "Lock object for domain %s is not currently locked", domainName);
|
||||
checkArgument(
|
||||
!lock.getUnlockRequestTimestamp().isPresent() || lock.isUnlockRequestExpired(clock),
|
||||
!lock.getUnlockRequestTimestamp().isPresent() || lock.isUnlockRequestExpired(now),
|
||||
"A pending unlock action already exists for %s",
|
||||
domainName);
|
||||
checkArgument(
|
||||
@@ -128,65 +281,12 @@ public final class DomainLockUtils {
|
||||
!lock.isSuperuser(), "Non-admin user cannot unlock admin-locked domain %s", domainName);
|
||||
newLockBuilder = lock.asBuilder();
|
||||
}
|
||||
RegistryLock newLock =
|
||||
newLockBuilder
|
||||
.setVerificationCode(stringGenerator.createString(VERIFICATION_CODE_LENGTH))
|
||||
.isSuperuser(isAdmin)
|
||||
.setUnlockRequestTimestamp(clock.nowUtc())
|
||||
.setRegistrarId(registrarId)
|
||||
.build();
|
||||
return RegistryLockDao.save(newLock);
|
||||
}
|
||||
|
||||
public RegistryLock verifyAndApplyLock(String verificationCode, boolean isAdmin, Clock clock) {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
RegistryLock lock = getByVerificationCode(verificationCode);
|
||||
|
||||
checkArgument(
|
||||
!lock.getLockCompletionTimestamp().isPresent(),
|
||||
"Domain %s is already locked",
|
||||
lock.getDomainName());
|
||||
|
||||
checkArgument(
|
||||
!lock.isLockRequestExpired(clock),
|
||||
"The pending lock has expired; please try again");
|
||||
|
||||
checkArgument(
|
||||
!lock.isSuperuser() || isAdmin, "Non-admin user cannot complete admin lock");
|
||||
|
||||
RegistryLock newLock =
|
||||
RegistryLockDao.save(
|
||||
lock.asBuilder().setLockCompletionTimestamp(clock.nowUtc()).build());
|
||||
tm().transact(() -> applyLockStatuses(newLock, clock));
|
||||
return newLock;
|
||||
});
|
||||
}
|
||||
|
||||
public RegistryLock verifyAndApplyUnlock(String verificationCode, boolean isAdmin, Clock clock) {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
RegistryLock lock = getByVerificationCode(verificationCode);
|
||||
checkArgument(
|
||||
!lock.getUnlockCompletionTimestamp().isPresent(),
|
||||
"Domain %s is already unlocked",
|
||||
lock.getDomainName());
|
||||
|
||||
checkArgument(
|
||||
!lock.isUnlockRequestExpired(clock),
|
||||
"The pending unlock has expired; please try again");
|
||||
|
||||
checkArgument(
|
||||
isAdmin || !lock.isSuperuser(), "Non-admin user cannot complete admin unlock");
|
||||
|
||||
RegistryLock newLock =
|
||||
RegistryLockDao.save(
|
||||
lock.asBuilder().setUnlockCompletionTimestamp(clock.nowUtc()).build());
|
||||
tm().transact(() -> removeLockStatuses(newLock, isAdmin, clock));
|
||||
return newLock;
|
||||
});
|
||||
relockDuration.ifPresent(newLockBuilder::setRelockDuration);
|
||||
return newLockBuilder
|
||||
.setVerificationCode(stringGenerator.createString(VERIFICATION_CODE_LENGTH))
|
||||
.isSuperuser(isAdmin)
|
||||
.setUnlockRequestTimestamp(now)
|
||||
.setRegistrarId(registrarId);
|
||||
}
|
||||
|
||||
private static void verifyDomainNotLocked(DomainBase domainBase) {
|
||||
@@ -203,8 +303,8 @@ public final class DomainLockUtils {
|
||||
domainBase.getFullyQualifiedDomainName());
|
||||
}
|
||||
|
||||
private static DomainBase getDomain(String domainName, Clock clock) {
|
||||
return loadByForeignKeyCached(DomainBase.class, domainName, clock.nowUtc())
|
||||
private static DomainBase getDomain(String domainName, DateTime now) {
|
||||
return loadByForeignKeyCached(DomainBase.class, domainName, now)
|
||||
.orElseThrow(
|
||||
() -> new IllegalArgumentException(String.format("Unknown domain %s", domainName)));
|
||||
}
|
||||
@@ -217,8 +317,8 @@ public final class DomainLockUtils {
|
||||
String.format("Invalid verification code %s", verificationCode)));
|
||||
}
|
||||
|
||||
private static void applyLockStatuses(RegistryLock lock, Clock clock) {
|
||||
DomainBase domain = getDomain(lock.getDomainName(), clock);
|
||||
private static void applyLockStatuses(RegistryLock lock, DateTime lockTime) {
|
||||
DomainBase domain = getDomain(lock.getDomainName(), lockTime);
|
||||
verifyDomainNotLocked(domain);
|
||||
|
||||
DomainBase newDomain =
|
||||
@@ -227,11 +327,11 @@ public final class DomainLockUtils {
|
||||
.setStatusValues(
|
||||
ImmutableSet.copyOf(Sets.union(domain.getStatusValues(), REGISTRY_LOCK_STATUSES)))
|
||||
.build();
|
||||
saveEntities(newDomain, lock, clock);
|
||||
saveEntities(newDomain, lock, lockTime, true);
|
||||
}
|
||||
|
||||
private static void removeLockStatuses(RegistryLock lock, boolean isAdmin, Clock clock) {
|
||||
DomainBase domain = getDomain(lock.getDomainName(), clock);
|
||||
private static void removeLockStatuses(RegistryLock lock, boolean isAdmin, DateTime unlockTime) {
|
||||
DomainBase domain = getDomain(lock.getDomainName(), unlockTime);
|
||||
if (!isAdmin) {
|
||||
verifyDomainLocked(domain);
|
||||
}
|
||||
@@ -243,18 +343,21 @@ public final class DomainLockUtils {
|
||||
ImmutableSet.copyOf(
|
||||
Sets.difference(domain.getStatusValues(), REGISTRY_LOCK_STATUSES)))
|
||||
.build();
|
||||
saveEntities(newDomain, lock, clock);
|
||||
saveEntities(newDomain, lock, unlockTime, false);
|
||||
}
|
||||
|
||||
private static void saveEntities(DomainBase domain, RegistryLock lock, Clock clock) {
|
||||
String reason = "Lock or unlock of a domain through a RegistryLock operation";
|
||||
private static void saveEntities(
|
||||
DomainBase domain, RegistryLock lock, DateTime now, boolean isLock) {
|
||||
String reason =
|
||||
String.format(
|
||||
"%s of a domain through a RegistryLock operation", isLock ? "Lock" : "Unlock");
|
||||
HistoryEntry historyEntry =
|
||||
new HistoryEntry.Builder()
|
||||
.setClientId(domain.getCurrentSponsorClientId())
|
||||
.setBySuperuser(lock.isSuperuser())
|
||||
.setRequestedByRegistrar(!lock.isSuperuser())
|
||||
.setType(HistoryEntry.Type.DOMAIN_UPDATE)
|
||||
.setModificationTime(clock.nowUtc())
|
||||
.setModificationTime(now)
|
||||
.setParent(Key.create(domain))
|
||||
.setReason(reason)
|
||||
.build();
|
||||
@@ -266,8 +369,8 @@ public final class DomainLockUtils {
|
||||
.setTargetId(domain.getForeignKey())
|
||||
.setClientId(domain.getCurrentSponsorClientId())
|
||||
.setCost(Registry.get(domain.getTld()).getServerStatusChangeCost())
|
||||
.setEventTime(clock.nowUtc())
|
||||
.setBillingTime(clock.nowUtc())
|
||||
.setEventTime(now)
|
||||
.setBillingTime(now)
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
ofy().save().entity(oneTime);
|
||||
|
||||
@@ -28,11 +28,13 @@ import com.beust.jcommander.ParameterException;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.appengine.api.taskqueue.Queue;
|
||||
import com.google.appengine.api.taskqueue.TaskOptions;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import google.registry.model.rde.RdeMode;
|
||||
import google.registry.rde.RdeStagingAction;
|
||||
import google.registry.tools.params.DateTimeParameter;
|
||||
import google.registry.util.AppEngineServiceUtils;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
@@ -75,7 +77,16 @@ final class GenerateEscrowDepositCommand implements CommandWithRemoteApi {
|
||||
private String outdir;
|
||||
|
||||
@Inject AppEngineServiceUtils appEngineServiceUtils;
|
||||
@Inject @Named("rde-report") Queue queue;
|
||||
|
||||
@Inject
|
||||
@Named("rde-report")
|
||||
Queue queue;
|
||||
|
||||
// ETA is a required property for TaskOptions but we let the service to set it when submitting the
|
||||
// task to the task queue. However, the local test service doesn't do that for us during the unit
|
||||
// test, so we add this field here to let the unit test be able to inject the ETA to pass the
|
||||
// test.
|
||||
@VisibleForTesting Optional<Long> maybeEtaMillis = Optional.empty();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -115,6 +126,9 @@ final class GenerateEscrowDepositCommand implements CommandWithRemoteApi {
|
||||
if (revision != null) {
|
||||
opts = opts.param(PARAM_REVISION, String.valueOf(revision));
|
||||
}
|
||||
if (maybeEtaMillis.isPresent()) {
|
||||
opts = opts.etaMillis(maybeEtaMillis.get());
|
||||
}
|
||||
queue.add(opts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import com.google.common.collect.Sets;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.schema.domain.RegistryLock;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
@@ -36,35 +35,24 @@ public class LockDomainCommand extends LockOrUnlockDomainCommand {
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@Override
|
||||
protected ImmutableSet<String> getRelevantDomains() {
|
||||
// Project all domains as of the same time so that argument order doesn't affect behavior.
|
||||
DateTime now = clock.nowUtc();
|
||||
ImmutableSet.Builder<String> relevantDomains = new ImmutableSet.Builder<>();
|
||||
for (String domain : getDomains()) {
|
||||
DomainBase domainBase =
|
||||
loadByForeignKey(DomainBase.class, domain, now)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format("Domain '%s' does not exist or is deleted", domain)));
|
||||
ImmutableSet<StatusValue> statusesToAdd =
|
||||
Sets.difference(REGISTRY_LOCK_STATUSES, domainBase.getStatusValues()).immutableCopy();
|
||||
if (statusesToAdd.isEmpty()) {
|
||||
logger.atInfo().log("Domain '%s' is already locked and needs no updates.", domain);
|
||||
continue;
|
||||
}
|
||||
relevantDomains.add(domain);
|
||||
protected boolean shouldApplyToDomain(String domain, DateTime now) {
|
||||
DomainBase domainBase =
|
||||
loadByForeignKey(DomainBase.class, domain, now)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format("Domain '%s' does not exist or is deleted", domain)));
|
||||
ImmutableSet<StatusValue> statusesToAdd =
|
||||
Sets.difference(REGISTRY_LOCK_STATUSES, domainBase.getStatusValues()).immutableCopy();
|
||||
if (statusesToAdd.isEmpty()) {
|
||||
logger.atInfo().log("Domain '%s' is already locked and needs no updates.", domain);
|
||||
return false;
|
||||
}
|
||||
return relevantDomains.build();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RegistryLock createLock(String domain) {
|
||||
return domainLockUtils.createRegistryLockRequest(domain, clientId, null, true, clock);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalizeLockOrUnlockRequest(RegistryLock lock) {
|
||||
domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock);
|
||||
protected void createAndApplyRequest(String domain) {
|
||||
domainLockUtils.administrativelyApplyLock(domain, clientId, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,22 +15,22 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.Iterables.partition;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_DELETE_PROHIBITED;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_TRANSFER_PROHIBITED;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.CollectionUtils.findDuplicates;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.schema.domain.RegistryLock;
|
||||
import google.registry.util.Clock;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Shared base class for commands to registry lock or unlock a domain via EPP. */
|
||||
public abstract class LockOrUnlockDomainCommand extends ConfirmingCommand
|
||||
@@ -38,6 +38,8 @@ public abstract class LockOrUnlockDomainCommand extends ConfirmingCommand
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final int BATCH_SIZE = 10;
|
||||
|
||||
public static final ImmutableSet<StatusValue> REGISTRY_LOCK_STATUSES =
|
||||
ImmutableSet.of(
|
||||
SERVER_DELETE_PROHIBITED, SERVER_TRANSFER_PROHIBITED, SERVER_UPDATE_PROHIBITED);
|
||||
@@ -55,12 +57,8 @@ public abstract class LockOrUnlockDomainCommand extends ConfirmingCommand
|
||||
@Config("registryAdminClientId")
|
||||
String registryAdminClientId;
|
||||
|
||||
@Inject Clock clock;
|
||||
|
||||
@Inject DomainLockUtils domainLockUtils;
|
||||
|
||||
protected ImmutableSet<String> relevantDomains = ImmutableSet.of();
|
||||
|
||||
protected ImmutableSet<String> getDomains() {
|
||||
return ImmutableSet.copyOf(mainParameters);
|
||||
}
|
||||
@@ -75,34 +73,42 @@ public abstract class LockOrUnlockDomainCommand extends ConfirmingCommand
|
||||
checkArgument(duplicates.isEmpty(), "Duplicate domain arguments found: '%s'", duplicates);
|
||||
System.out.println(
|
||||
"== ENSURE THAT YOU HAVE AUTHENTICATED THE REGISTRAR BEFORE RUNNING THIS COMMAND ==");
|
||||
relevantDomains = getRelevantDomains();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute() {
|
||||
int failures = 0;
|
||||
for (String domain : relevantDomains) {
|
||||
try {
|
||||
RegistryLock lock = createLock(domain);
|
||||
finalizeLockOrUnlockRequest(lock);
|
||||
} catch (Throwable t) {
|
||||
Throwable rootCause = Throwables.getRootCause(t);
|
||||
logger.atSevere().withCause(rootCause).log("Error when (un)locking domain %s.", domain);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
if (failures == 0) {
|
||||
return String.format("Successfully locked/unlocked %d domains.", relevantDomains.size());
|
||||
} else {
|
||||
return String.format(
|
||||
"Successfully locked/unlocked %d domains with %d failures.",
|
||||
relevantDomains.size() - failures, failures);
|
||||
}
|
||||
ImmutableSet.Builder<String> successfulDomainsBuilder = new ImmutableSet.Builder<>();
|
||||
ImmutableSet.Builder<String> skippedDomainsBuilder = new ImmutableSet.Builder<>();
|
||||
ImmutableSet.Builder<String> failedDomainsBuilder = new ImmutableSet.Builder<>();
|
||||
partition(getDomains(), BATCH_SIZE)
|
||||
.forEach(
|
||||
batch ->
|
||||
tm().transact(
|
||||
() -> {
|
||||
for (String domain : batch) {
|
||||
if (shouldApplyToDomain(domain, tm().getTransactionTime())) {
|
||||
try {
|
||||
createAndApplyRequest(domain);
|
||||
} catch (Throwable t) {
|
||||
logger.atSevere().withCause(t).log(
|
||||
"Error when (un)locking domain %s.", domain);
|
||||
failedDomainsBuilder.add(domain);
|
||||
}
|
||||
successfulDomainsBuilder.add(domain);
|
||||
} else {
|
||||
skippedDomainsBuilder.add(domain);
|
||||
}
|
||||
}
|
||||
}));
|
||||
ImmutableSet<String> successfulDomains = successfulDomainsBuilder.build();
|
||||
ImmutableSet<String> skippedDomains = skippedDomainsBuilder.build();
|
||||
ImmutableSet<String> failedDomains = failedDomainsBuilder.build();
|
||||
return String.format(
|
||||
"Successfully locked/unlocked domains:\n%s\nSkipped domains:\n%s\nFailed domains:\n%s",
|
||||
successfulDomains, skippedDomains, failedDomains);
|
||||
}
|
||||
|
||||
protected abstract ImmutableSet<String> getRelevantDomains();
|
||||
protected abstract boolean shouldApplyToDomain(String domain, DateTime now);
|
||||
|
||||
protected abstract RegistryLock createLock(String domain);
|
||||
|
||||
protected abstract void finalizeLockOrUnlockRequest(RegistryLock lock);
|
||||
protected abstract void createAndApplyRequest(String domain);
|
||||
}
|
||||
|
||||
@@ -20,17 +20,18 @@ import com.google.storage.onestore.v3.OnestoreEntity.EntityProto;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/** Utility class that accumulates Entity records from level db files. */
|
||||
/** Accumulates Entity records from level db files under a directory hierarchy. */
|
||||
class RecordAccumulator {
|
||||
private final LevelDbLogReader reader = new LevelDbLogReader();
|
||||
|
||||
/** Recursively reads all records in the directory. */
|
||||
public final RecordAccumulator readDirectory(File dir) {
|
||||
public final RecordAccumulator readDirectory(File dir, Predicate<File> fileMatcher) {
|
||||
for (File child : dir.listFiles()) {
|
||||
if (child.isDirectory()) {
|
||||
readDirectory(child);
|
||||
} else if (child.isFile()) {
|
||||
readDirectory(child, fileMatcher);
|
||||
} else if (fileMatcher.test(child)) {
|
||||
try {
|
||||
reader.readFrom(new FileInputStream(child));
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -78,11 +78,15 @@ final class RegistrarContactCommand extends MutatingCommand {
|
||||
private List<String> contactTypeNames;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--email",
|
||||
description = "Contact email address.")
|
||||
@Parameter(names = "--email", description = "Contact email address.")
|
||||
String email;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--registry_lock_email",
|
||||
description = "Email address used for registry lock confirmation emails")
|
||||
String registryLockEmail;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--phone",
|
||||
@@ -247,6 +251,9 @@ final class RegistrarContactCommand extends MutatingCommand {
|
||||
builder.setParent(registrar);
|
||||
builder.setName(name);
|
||||
builder.setEmailAddress(email);
|
||||
if (!isNullOrEmpty(registryLockEmail)) {
|
||||
builder.setRegistryLockEmailAddress(registryLockEmail);
|
||||
}
|
||||
if (phone != null) {
|
||||
builder.setPhoneNumber(phone.orElse(null));
|
||||
}
|
||||
@@ -277,14 +284,14 @@ final class RegistrarContactCommand extends MutatingCommand {
|
||||
|
||||
private RegistrarContact updateContact(RegistrarContact contact, Registrar registrar) {
|
||||
checkNotNull(registrar);
|
||||
checkNotNull(email, "--email is required when --mode=UPDATE");
|
||||
RegistrarContact.Builder builder = contact.asBuilder();
|
||||
builder.setParent(registrar);
|
||||
checkArgument(!isNullOrEmpty(email), "--email is required when --mode=UPDATE");
|
||||
RegistrarContact.Builder builder =
|
||||
contact.asBuilder().setEmailAddress(email).setParent(registrar);
|
||||
if (!isNullOrEmpty(name)) {
|
||||
builder.setName(name);
|
||||
}
|
||||
if (!isNullOrEmpty(email)) {
|
||||
builder.setEmailAddress(email);
|
||||
if (!isNullOrEmpty(registryLockEmail)) {
|
||||
builder.setRegistryLockEmailAddress(registryLockEmail);
|
||||
}
|
||||
if (phone != null) {
|
||||
builder.setPhoneNumber(phone.orElse(null));
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.model.ofy.ObjectifyService;
|
||||
import google.registry.persistence.transaction.TransactionManagerFactory;
|
||||
import google.registry.tools.AuthModule.LoginRequiredException;
|
||||
import google.registry.tools.params.ParameterFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -237,7 +238,7 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||
// Enable Cloud SQL for command that needs remote API as they will very likely use
|
||||
// Cloud SQL after the database migration. Note that the DB password is stored in Datastore
|
||||
// and it is already initialized above.
|
||||
RegistryToolEnvironment.get().enableJpaTm();
|
||||
TransactionManagerFactory.setJpaTm(component.nomulusToolJpaTransactionManager());
|
||||
}
|
||||
|
||||
command.run();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.tools.javascrap.BackfillRegistryLocksCommand;
|
||||
import google.registry.tools.javascrap.PopulateNullRegistrarFieldsCommand;
|
||||
import google.registry.tools.javascrap.RemoveIpAddressCommand;
|
||||
|
||||
@@ -30,6 +31,7 @@ public final class RegistryTool {
|
||||
public static final ImmutableMap<String, Class<? extends Command>> COMMAND_MAP =
|
||||
new ImmutableMap.Builder<String, Class<? extends Command>>()
|
||||
.put("ack_poll_messages", AckPollMessagesCommand.class)
|
||||
.put("backfill_registry_locks", BackfillRegistryLocksCommand.class)
|
||||
.put("canonicalize_labels", CanonicalizeLabelsCommand.class)
|
||||
.put("check_domain", CheckDomainCommand.class)
|
||||
.put("check_domain_claims", CheckDomainClaimsCommand.class)
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.tools;
|
||||
|
||||
import dagger.BindsInstance;
|
||||
import dagger.Component;
|
||||
import google.registry.batch.BatchModule;
|
||||
import google.registry.bigquery.BigqueryModule;
|
||||
import google.registry.config.CredentialModule.LocalCredentialJson;
|
||||
import google.registry.config.RegistryConfig.ConfigModule;
|
||||
@@ -27,6 +28,9 @@ import google.registry.keyring.KeyringModule;
|
||||
import google.registry.keyring.api.DummyKeyringModule;
|
||||
import google.registry.keyring.api.KeyModule;
|
||||
import google.registry.keyring.kms.KmsModule;
|
||||
import google.registry.persistence.PersistenceModule;
|
||||
import google.registry.persistence.PersistenceModule.NomulusToolJpaTm;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import google.registry.rde.RdeModule;
|
||||
import google.registry.request.Modules.DatastoreServiceModule;
|
||||
import google.registry.request.Modules.Jackson2Module;
|
||||
@@ -51,6 +55,7 @@ import javax.inject.Singleton;
|
||||
modules = {
|
||||
AppEngineAdminApiModule.class,
|
||||
AuthModule.class,
|
||||
BatchModule.class,
|
||||
BigqueryModule.class,
|
||||
ConfigModule.class,
|
||||
CloudDnsWriterModule.class,
|
||||
@@ -63,6 +68,7 @@ import javax.inject.Singleton;
|
||||
KeyringModule.class,
|
||||
KmsModule.class,
|
||||
LocalCredentialModule.class,
|
||||
PersistenceModule.class,
|
||||
RdeModule.class,
|
||||
RequestFactoryModule.class,
|
||||
URLFetchServiceModule.class,
|
||||
@@ -70,7 +76,7 @@ import javax.inject.Singleton;
|
||||
UserServiceModule.class,
|
||||
UtilsModule.class,
|
||||
VoidDnsWriterModule.class,
|
||||
WhoisModule.class,
|
||||
WhoisModule.class
|
||||
})
|
||||
interface RegistryToolComponent {
|
||||
void inject(AckPollMessagesCommand command);
|
||||
@@ -117,6 +123,9 @@ interface RegistryToolComponent {
|
||||
@LocalCredentialJson
|
||||
String googleCredentialJson();
|
||||
|
||||
@NomulusToolJpaTm
|
||||
JpaTransactionManager nomulusToolJpaTransactionManager();
|
||||
|
||||
@Component.Builder
|
||||
interface Builder {
|
||||
@BindsInstance
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.config.SystemPropertySetter;
|
||||
import google.registry.persistence.transaction.TransactionManagerFactory;
|
||||
|
||||
/** Enum of production environments, used for the {@code --environment} flag. */
|
||||
public enum RegistryToolEnvironment {
|
||||
@@ -40,7 +39,6 @@ public enum RegistryToolEnvironment {
|
||||
|
||||
private static final ImmutableList<String> FLAGS = ImmutableList.of("-e", "--environment");
|
||||
private static RegistryToolEnvironment instance;
|
||||
private static boolean isJpaTmEnabled = false;
|
||||
private final RegistryEnvironment actualEnvironment;
|
||||
private final ImmutableMap<String, String> extraProperties;
|
||||
|
||||
@@ -100,26 +98,6 @@ public enum RegistryToolEnvironment {
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if the RegistryToolEnvironment is set up. */
|
||||
public static boolean isInRegistryTool() {
|
||||
return instance != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the flag to indicate that the running command needs JpaTransactionManager to be enabled.
|
||||
*/
|
||||
public static void enableJpaTm() {
|
||||
isJpaTmEnabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the JpaTransactionManager is enabled. Note that JpaTm is actually enabled in
|
||||
* {@link TransactionManagerFactory} by reading this flag.
|
||||
*/
|
||||
public static boolean isJpaTmEnabled() {
|
||||
return isJpaTmEnabled;
|
||||
}
|
||||
|
||||
/** Extracts value from command-line arguments associated with any {@code flags}. */
|
||||
private static String getFlagValue(String[] args, Iterable<String> flags) {
|
||||
for (String flag : flags) {
|
||||
|
||||
@@ -19,7 +19,7 @@ import static com.google.common.base.Strings.nullToEmpty;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static google.registry.model.EppResourceUtils.checkResourcesExist;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
@@ -149,7 +149,7 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
|
||||
|
||||
private ImmutableSortedSet<String> getExistingNameservers(DomainBase domain) {
|
||||
ImmutableSortedSet.Builder<String> nameservers = ImmutableSortedSet.naturalOrder();
|
||||
for (HostResource host : ofy().load().keys(domain.getNameservers()).values()) {
|
||||
for (HostResource host : tm().load(domain.getNameservers())) {
|
||||
nameservers.add(host.getForeignKey());
|
||||
}
|
||||
return nameservers.build();
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.google.common.collect.Sets;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.schema.domain.RegistryLock;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
@@ -36,35 +36,24 @@ public class UnlockDomainCommand extends LockOrUnlockDomainCommand {
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@Override
|
||||
protected ImmutableSet<String> getRelevantDomains() {
|
||||
// Project all domains as of the same time so that argument order doesn't affect behavior.
|
||||
DateTime now = clock.nowUtc();
|
||||
ImmutableSet.Builder<String> relevantDomains = new ImmutableSet.Builder<>();
|
||||
for (String domain : getDomains()) {
|
||||
DomainBase domainBase =
|
||||
loadByForeignKey(DomainBase.class, domain, now)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format("Domain '%s' does not exist or is deleted", domain)));
|
||||
ImmutableSet<StatusValue> statusesToRemove =
|
||||
Sets.intersection(domainBase.getStatusValues(), REGISTRY_LOCK_STATUSES).immutableCopy();
|
||||
if (statusesToRemove.isEmpty()) {
|
||||
logger.atInfo().log("Domain '%s' is already unlocked and needs no updates.", domain);
|
||||
continue;
|
||||
}
|
||||
relevantDomains.add(domain);
|
||||
protected boolean shouldApplyToDomain(String domain, DateTime now) {
|
||||
DomainBase domainBase =
|
||||
loadByForeignKey(DomainBase.class, domain, now)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format("Domain '%s' does not exist or is deleted", domain)));
|
||||
ImmutableSet<StatusValue> statusesToRemove =
|
||||
Sets.intersection(domainBase.getStatusValues(), REGISTRY_LOCK_STATUSES).immutableCopy();
|
||||
if (statusesToRemove.isEmpty()) {
|
||||
logger.atInfo().log("Domain '%s' is already unlocked and needs no updates.", domain);
|
||||
return false;
|
||||
}
|
||||
return relevantDomains.build();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RegistryLock createLock(String domain) {
|
||||
return domainLockUtils.createRegistryUnlockRequest(domain, clientId, true, clock);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalizeLockOrUnlockRequest(RegistryLock lock) {
|
||||
domainLockUtils.verifyAndApplyUnlock(lock.getVerificationCode(), true, clock);
|
||||
protected void createAndApplyRequest(String domain) {
|
||||
domainLockUtils.administrativelyApplyUnlock(domain, clientId, true, Optional.empty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.schema.registrar.RegistrarDao;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Command to update a Registrar. */
|
||||
@@ -53,6 +53,6 @@ final class UpdateRegistrarCommand extends CreateOrUpdateRegistrarCommand {
|
||||
|
||||
@Override
|
||||
void saveToCloudSql(Registrar registrar) {
|
||||
RegistrarDao.update(registrar);
|
||||
jpaTm().update(registrar);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user