mirror of
https://github.com/google/nomulus
synced 2026-07-26 10:03:23 +00:00
Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e0489e5f9 | ||
|
|
d29a98bdd3 | ||
|
|
d6dd95b052 | ||
|
|
6b74924067 | ||
|
|
71e9bc95a7 | ||
|
|
bf54a0d0c4 | ||
|
|
2a04b9be9b | ||
|
|
675354ae65 | ||
|
|
21421726e0 | ||
|
|
192a7e6c4e | ||
|
|
542084eb70 | ||
|
|
54e605fa27 | ||
|
|
188670e156 | ||
|
|
2fd609ed03 | ||
|
|
fc805ba4d9 | ||
|
|
21976bee24 | ||
|
|
c2bd11c528 | ||
|
|
653da19e53 | ||
|
|
4df734da2a | ||
|
|
b8a51cacc4 | ||
|
|
5067b3d339 | ||
|
|
93ec04018f |
@@ -72,10 +72,19 @@ This document outlines foundational mandates, architectural patterns, and projec
|
||||
- **No CodeSearch:** This project is hosted on GitHub, not Google3. Do NOT use `mcp_Coding_search_for_files_codesearch` or other internal Google3 search tools.
|
||||
- **Local Grep:** Use local shell commands like `git grep` or `grep` via `run_shell_command` to search the codebase.
|
||||
|
||||
### 8. Git Operation Boundaries & Remote Push Prohibition
|
||||
- **Zero-Bundling Rule for Remote Transfers:** NEVER bundle remote transfer commands (`git push`, `repo upload`, `g4 upload`, `g4 mail`, `hg push`, `jj git push`) inside compound bash pipelines (`&&`, `||`, `;`) with local staging or commit commands (e.g., `git commit --amend && git push ...`). Every remote transfer command MUST be executed as a standalone, single-command `run_command` invocation.
|
||||
- **Mandatory Two-Step Handover Protocol:** When working on pull requests, changelists, or branches, strictly halt right at the local repository boundary (`git commit` / `g4 change`). Report the local commit SHA, `git status`, and `git diff` verification to the user, stop calling tools, and explicitly prompt: *"Local commit `[SHA]` is verified. Do you authorize running `git push origin [branch]` (or `--force-with-lease` when force-pushing an amended branch) to update the remote repository?"*
|
||||
- **Absolute Prohibition on Unsolicited Pushes:** NEVER propose or run any remote synchronization or push command (`git push`, `repo upload`, `g4 upload`) on your own volition without explicit, unambiguous textual authorization in the immediate turn from the user.
|
||||
- **Mandatory GitHub PR Description & Reviewable Synchronization:** Whenever pushing an amended commit to a branch associated with an active pull request, the agent MUST immediately synchronize the PR description on GitHub (`gh pr edit`). To do this safely:
|
||||
1. Check `gh pr view --json body` first to inspect existing content.
|
||||
2. Explicitly preserve any existing Reviewable bot links (`This change is https://reviewable.io/reviews/...`) when updating the body rather than blindly overwriting the entire description.
|
||||
|
||||
## Performance and Efficiency
|
||||
- **Turn Minimization:** Aim for "perfect" code in the first iteration. Iterative fixes for checkstyle or compilation errors consume significant context and time.
|
||||
- **Context Management:** Use sub-agents for batch refactoring or high-volume output tasks to keep the main session history lean and efficient.
|
||||
- **Code Formatting:** Do not write custom Python scripts or manual regex replacements to fix code formatting issues (e.g., unused imports, import ordering, line length). Instead, use the project's built-in formatting tools: run `./gradlew spotlessApply` to fix unused/unordered imports and `./gradlew javaIncrementalFormatApply` (or `google-java-format --replace <files>`) to automatically fix Java formatting and indentation errors.
|
||||
- **Programmatic Measurement for Markdown:** When writing Markdown files (`.md`)—and especially when formatting or line-wrapping them—you **MUST** explicitly use programmatic functions (such as `awk '{print length($0)}'` or Python `len(line)`) to measure the exact string length of every line from column 1, rather than making assumptions based on what appears in your context window.
|
||||
|
||||
## General Code Review Lessons & Avoidable Mistakes
|
||||
Based on historical PR reviews, avoid the following common mistakes:
|
||||
|
||||
+1
-1
@@ -395,7 +395,7 @@ subprojects {
|
||||
// expose to users.
|
||||
if (project.name != 'jetty' && !services.contains(project.path)) {
|
||||
javadocSource << project.sourceSets.main.allJava
|
||||
javadocClasspath << { project.sourceSets.main.runtimeClasspath.files }
|
||||
javadocClasspath << { project.sourceSets.main.compileClasspath.files }
|
||||
javadocClasspath << "${buildDir}/generated/sources/annotationProcessor/java/main"
|
||||
if (project.tasks.findByName('compileJava')) {
|
||||
javadocDependentTasks << project.tasks.compileJava
|
||||
|
||||
@@ -68,12 +68,12 @@ org.jacoco:org.jacoco.core:0.8.14=jacocoAnt
|
||||
org.jacoco:org.jacoco.report:0.8.14=jacocoAnt
|
||||
org.javassist:javassist:3.28.0-GA=checkstyle
|
||||
org.jspecify:jspecify:1.0.0=annotationProcessor,checkstyle,compileClasspath,deploy_jar,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath,testing,testingAnnotationProcessor,testingCompileClasspath
|
||||
org.junit.jupiter:junit-jupiter-api:6.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-engine:6.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-commons:6.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-engine:6.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-launcher:6.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit:junit-bom:6.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-api:6.1.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-engine:6.1.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-commons:6.1.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-engine:6.1.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-launcher:6.1.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit:junit-bom:6.1.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.ow2.asm:asm-commons:9.9=jacocoAnt
|
||||
org.ow2.asm:asm-tree:9.9=jacocoAnt
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<p>
|
||||
<mat-label for="password">Password: </mat-label>
|
||||
<mat-form-field name="password" appearance="outline">
|
||||
<input matInput type="text" formControlName="password" required />
|
||||
<input matInput type="password" formControlName="password" required />
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<p>
|
||||
@@ -60,7 +60,7 @@
|
||||
<p>
|
||||
<mat-label for="password">Password: </mat-label>
|
||||
<mat-form-field name="password" appearance="outline">
|
||||
<input matInput type="text" formControlName="password" required />
|
||||
<input matInput type="password" formControlName="password" required />
|
||||
</mat-form-field>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { Component, computed } from '@angular/core';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { RegistrarService } from '../registrar/registrar.service';
|
||||
import { UserDataService } from '../shared/services/userData.service';
|
||||
@@ -42,11 +42,11 @@ export class RegistryLockComponent {
|
||||
];
|
||||
|
||||
lockDomain = new FormGroup({
|
||||
password: new FormControl(''),
|
||||
password: new FormControl('', [Validators.required]),
|
||||
});
|
||||
|
||||
unlockDomain = new FormGroup({
|
||||
password: new FormControl(''),
|
||||
password: new FormControl('', [Validators.required]),
|
||||
relockTime: new FormControl(undefined),
|
||||
});
|
||||
|
||||
|
||||
@@ -50,7 +50,10 @@ export class NewOteComponent {
|
||||
|
||||
createOte = new FormGroup({
|
||||
registrarId: new FormControl('', [Validators.required]),
|
||||
registrarEmail: new FormControl('', [Validators.required]),
|
||||
registrarEmail: new FormControl('', [
|
||||
Validators.required,
|
||||
Validators.email,
|
||||
]),
|
||||
});
|
||||
|
||||
constructor(
|
||||
|
||||
@@ -67,8 +67,10 @@ export default class NewRegistrarComponent {
|
||||
onBillingAccountMapChange(val: String) {
|
||||
const billingAccountMap: { [key: string]: string } = {};
|
||||
this.newRegistrar.billingAccountMap = val.split('\n').reduce((acc, val) => {
|
||||
const [currency, billingCode] = val.split('=');
|
||||
acc[currency] = billingCode;
|
||||
const [currency, billingCode] = val.split('=').map((s) => s?.trim());
|
||||
if (currency && billingCode) {
|
||||
acc[currency] = billingCode;
|
||||
}
|
||||
return acc;
|
||||
}, billingAccountMap);
|
||||
}
|
||||
|
||||
+3
-3
@@ -9,7 +9,7 @@
|
||||
<mat-label>Old password: </mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="text"
|
||||
type="password"
|
||||
formControlName="oldPassword"
|
||||
required
|
||||
autocomplete="current-password"
|
||||
@@ -25,7 +25,7 @@
|
||||
<mat-label>New password: </mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="text"
|
||||
type="password"
|
||||
formControlName="newPassword"
|
||||
required
|
||||
autocomplete="new-password"
|
||||
@@ -40,7 +40,7 @@
|
||||
<mat-label>Confirm new password: </mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="text"
|
||||
type="password"
|
||||
formControlName="newPasswordRepeat"
|
||||
required
|
||||
autocomplete="new-password"
|
||||
|
||||
@@ -288,8 +288,9 @@ export class BackendService {
|
||||
verifyRegistryLockRequest(
|
||||
lockVerificationCode: string
|
||||
): Observable<RegistryLockVerificationResponse> {
|
||||
return this.http.get<RegistryLockVerificationResponse>(
|
||||
`/console-api/registry-lock-verify?lockVerificationCode=${lockVerificationCode}`
|
||||
return this.http.post<RegistryLockVerificationResponse>(
|
||||
`/console-api/registry-lock-verify?lockVerificationCode=${lockVerificationCode}`,
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -229,9 +229,9 @@ dependencies {
|
||||
runtimeOnly deps['org.slf4j:slf4j-jdk14']
|
||||
testImplementation deps['org.testcontainers:jdbc']
|
||||
testImplementation deps['org.testcontainers:junit-jupiter']
|
||||
implementation deps['org.testcontainers:postgresql']
|
||||
nonprodImplementation deps['org.testcontainers:postgresql']
|
||||
testImplementation deps['org.testcontainers:selenium']
|
||||
testImplementation deps['org.testcontainers:testcontainers']
|
||||
nonprodImplementation deps['org.testcontainers:testcontainers']
|
||||
implementation deps['redis.clients:jedis']
|
||||
implementation deps['us.fatehi:schemacrawler']
|
||||
implementation deps['us.fatehi:schemacrawler-api']
|
||||
|
||||
+257
-255
@@ -3,20 +3,20 @@
|
||||
# This file is expected to be part of source control.
|
||||
args4j:args4j:2.33=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.charleskorn.kaml:kaml:0.20.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-annotations:2.21=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-core:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-databind:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.datatype:jackson-datatype-joda:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson:jackson-bom:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-annotations:2.22=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-core:2.22.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-databind:2.22.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.22.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.22.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.datatype:jackson-datatype-joda:2.22.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.22.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson:jackson-bom:2.22.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.woodstox:woodstox-core:7.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.5=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.github.ben-manes.caffeine:caffeine:3.2.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.docker-java:docker-java-api:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.docker-java:docker-java-transport:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.docker-java:docker-java-api:3.4.2=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.4.2=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.docker-java:docker-java-transport:3.4.2=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.jnr:jffi:1.3.15=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.jnr:jnr-a64asm:1.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.jnr:jnr-constants:0.10.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -26,71 +26,70 @@ com.github.jnr:jnr-posix:3.1.22=compileClasspath,deploy_jar,nonprodCompileClassp
|
||||
com.github.jnr:jnr-unixsocket:0.38.25=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.jnr:jnr-x86asm:1.0.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.github.mwiede:jsch:2.28.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.mwiede:jsch:2.28.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.android:annotations:4.1.1.4=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api-client:google-api-client-jackson2:2.0.1=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api-client:google-api-client-jackson2:2.7.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api-client:google-api-client-gson:2.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api-client:google-api-client-java6:2.1.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api-client:google-api-client-servlet:2.7.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api-client:google-api-client-servlet:2.9.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api-client:google-api-client:2.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:gapic-google-cloud-storage-v2:2.64.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:gapic-google-cloud-storage-v2:2.68.0=testCompileClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.24.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.196.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.196.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigtable-v2:2.73.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-pubsub-v1:1.132.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-spanner-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-storage-control-v2:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-storage-v2:2.64.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-storage-v2:2.68.0=testCompileClasspath
|
||||
com.google.api.grpc:grpc-google-common-protos:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1:3.24.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1alpha:3.24.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta1:0.196.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta2:0.196.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta:3.24.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigtable-admin-v2:2.73.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigtable-v2:2.75.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-compute-v1:1.102.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-datastore-v1:0.128.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-firestore-v1:3.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-logging-v2:0.118.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-monitoring-v3:3.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-pubsub-v1:1.132.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:gapic-google-cloud-storage-v2:2.68.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:gapic-google-cloud-storage-v2:2.70.0=testCompileClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.200.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.200.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigtable-v2:2.78.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-pubsub-v1:1.132.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1:6.117.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.117.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-spanner-v1:6.117.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-storage-control-v2:2.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-storage-v2:2.68.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-storage-v2:2.70.0=testCompileClasspath
|
||||
com.google.api.grpc:grpc-google-common-protos:2.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1:3.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1alpha:3.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta1:0.200.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta2:0.200.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta:3.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigtable-admin-v2:2.78.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigtable-v2:2.78.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-compute-v1:1.104.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-datastore-v1:0.133.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-firestore-v1:3.42.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-logging-v2:0.122.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-monitoring-v3:3.93.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-pubsub-v1:1.132.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.94.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:2.94.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-spanner-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-storage-control-v2:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-storage-v2:2.64.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-storage-v2:2.68.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.94.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:6.117.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:6.117.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-spanner-v1:6.117.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-storage-control-v2:2.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-storage-v2:2.68.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-storage-v2:2.70.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2:2.94.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.141.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.182.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:2.94.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.141.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.182.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-common-protos:2.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-iam-v1:1.62.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.api.grpc:proto-google-iam-v1:1.66.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:api-common:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:gax-grpc:2.80.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:gax-httpjson:2.80.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:gax:2.80.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-admin-directory:directory_v1-rev20260522-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:2.94.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-common-protos:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-iam-v1:1.66.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.api.grpc:proto-google-iam-v1:1.68.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:api-common:2.65.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:gax-grpc:2.82.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:gax-httpjson:2.82.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:gax:2.82.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-admin-directory:directory_v1-rev20260531-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-bigquery:v2-rev20251012-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-cloudresourcemanager:v1-rev20250606-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-dataflow:v1b3-rev20260503-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-dns:v1-rev20260421-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-drive:v3-rev20260428-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-dataflow:v1b3-rev20260615-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-dns:v1-rev20260616-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-drive:v3-rev20260624-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-gmail:v1-rev20260525-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-groupssettings:v1-rev20220614-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-healthcare:v1-rev20240130-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -98,11 +97,12 @@ com.google.apis:google-api-services-iam:v2-rev20250502-2.0.0=compileClasspath,de
|
||||
com.google.apis:google-api-services-iamcredentials:v1-rev20211203-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-monitoring:v3-rev20260129-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-pubsub:v1-rev20220904-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-sheets:v4-rev20260213-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-sqladmin:v1beta4-rev20260510-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-storage:v1-rev20260204-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auth:google-auth-library-credentials:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auth:google-auth-library-oauth2-http:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-sheets:v4-rev20260610-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-sqladmin:v1beta4-rev20260529-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-storage:v1-rev20260204-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.apis:google-api-services-storage:v1-rev20260524-2.0.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auth:google-auth-library-credentials:1.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auth:google-auth-library-oauth2-http:1.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auto.service:auto-service-annotations:1.0.1=nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.auto.service:auto-service-annotations:1.1.1=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auto.service:auto-service:1.1.1=annotationProcessor
|
||||
@@ -110,47 +110,48 @@ com.google.auto.value:auto-value-annotations:1.11.0=compileClasspath,deploy_jar,
|
||||
com.google.auto.value:auto-value-annotations:1.9=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.auto.value:auto-value:1.11.1=annotationProcessor,deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testRuntimeClasspath
|
||||
com.google.auto:auto-common:1.2.2=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.cloud.bigdataoss:gcsio:2.2.26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.bigdataoss:util:2.2.26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.bigdataoss:gcsio:3.1.16=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.bigdataoss:util:3.1.16=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.bigtable:bigtable-client-core-config:1.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.datastore:datastore-v1-proto-client:2.37.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.datastore:datastore-v1-proto-client:3.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.opentelemetry:detector-resources-support:0.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.opentelemetry:exporter-metrics:0.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.opentelemetry:shared-resourcemapping:0.33.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud.sql:jdbc-socket-factory-core:1.28.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.sql:postgres-socket-factory:1.28.4=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-bigquerystorage:3.24.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-bigtable:2.73.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-compute:1.102.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core-grpc:2.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.cloud:google-cloud-core-grpc:2.70.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core-http:2.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core-http:2.70.0=testCompileClasspath
|
||||
com.google.cloud:google-cloud-core:2.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.cloud:google-cloud-core:2.70.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-firestore:3.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-logging:3.29.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-monitoring:3.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.sql:jdbc-socket-factory-core:1.28.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.sql:postgres-socket-factory:1.28.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud.sql:schemas:1.28.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-bigquerystorage:3.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-bigtable:2.78.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-compute:1.104.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core-grpc:2.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.cloud:google-cloud-core-grpc:2.72.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core-http:2.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core-http:2.72.0=testCompileClasspath
|
||||
com.google.cloud:google-cloud-core:2.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.cloud:google-cloud-core:2.72.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-firestore:3.42.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-logging:3.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-monitoring:3.93.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-nio:0.127.24=testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-nio:0.132.0=testCompileClasspath
|
||||
com.google.cloud:google-cloud-pubsub:1.150.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-nio:0.134.0=testCompileClasspath
|
||||
com.google.cloud:google-cloud-pubsub:1.150.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-secretmanager:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-secretmanager:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.cloud:google-cloud-spanner:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-storage-control:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-storage:2.64.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-storage:2.68.0=testCompileClasspath
|
||||
com.google.cloud:google-cloud-secretmanager:2.94.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.cloud:google-cloud-spanner:6.117.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-storage-control:2.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-storage:2.68.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-storage:2.70.0=testCompileClasspath
|
||||
com.google.cloud:google-cloud-tasks:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-tasks:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.cloud:grpc-gcp:1.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-tasks:2.94.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.cloud:grpc-gcp:1.10.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:libraries-bom:26.48.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.42.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.code.gson:gson:2.14.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.dagger:dagger-compiler:2.59.2=annotationProcessor,testAnnotationProcessor
|
||||
com.google.dagger:dagger-spi:2.59.2=annotationProcessor,testAnnotationProcessor
|
||||
com.google.dagger:dagger:2.59.2=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.devtools.ksp:symbol-processing-api:2.2.20-2.0.3=annotationProcessor,testAnnotationProcessor
|
||||
com.google.dagger:dagger-compiler:2.60.1=annotationProcessor,testAnnotationProcessor
|
||||
com.google.dagger:dagger-spi:2.60.1=annotationProcessor,testAnnotationProcessor
|
||||
com.google.dagger:dagger:2.60.1=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.devtools.ksp:symbol-processing-api:2.3.7=annotationProcessor,testAnnotationProcessor
|
||||
com.google.errorprone:error_prone_annotation:2.48.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.errorprone:error_prone_annotations:2.36.0=checkstyle
|
||||
com.google.errorprone:error_prone_annotations:2.48.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
@@ -160,21 +161,24 @@ com.google.errorprone:error_prone_core:2.48.0=annotationProcessor,nonprodAnnotat
|
||||
com.google.flatbuffers:flatbuffers-java:24.3.25=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.flogger:flogger-system-backend:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.flogger:flogger:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.flogger:google-extensions:0.7.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.flogger:google-extensions:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.googlejavaformat:google-java-format:1.34.1=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.guava:failureaccess:1.0.3=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.guava:guava-testlib:33.3.0-jre=testRuntimeClasspath
|
||||
com.google.guava:guava-testlib:33.6.0-jre=testCompileClasspath
|
||||
com.google.guava:guava:33.4.8-jre=checkstyle
|
||||
com.google.guava:guava:33.5.0-jre=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.guava:guava:33.6.0-jre=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.guava:guava:33.5.0-jre=nonprodAnnotationProcessor
|
||||
com.google.guava:guava:33.6.0-jre=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-apache-v2:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-appengine:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-gson:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-jackson2:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-apache-v2:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.http-client:google-http-client-apache-v2:2.1.1=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-appengine:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-appengine:2.1.1=testCompileClasspath
|
||||
com.google.http-client:google-http-client-gson:2.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-jackson2:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-jackson2:2.1.1=testCompileClasspath
|
||||
com.google.http-client:google-http-client-protobuf:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client:2.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.j2objc:j2objc-annotations:3.0.0=checkstyle
|
||||
com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.monitoring-client:contrib:1.0.7=testCompileClasspath,testRuntimeClasspath
|
||||
@@ -189,19 +193,19 @@ com.google.oauth-client:google-oauth-client-servlet:1.39.0=compileClasspath,nonp
|
||||
com.google.oauth-client:google-oauth-client:1.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.protobuf:protobuf-java-util:4.33.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.protobuf:protobuf-java:4.35.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.protobuf:protobuf-java:4.35.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.re2j:re2j:1.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.truth:truth:1.4.5=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.ibm.icu:icu4j:73.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.lmax:disruptor:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.puppycrawl.tools:checkstyle:10.24.0=checkstyle
|
||||
com.squareup.okhttp3:okhttp-jvm:5.3.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.squareup.okhttp3:okhttp:5.3.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.squareup.okhttp3:okhttp-jvm:5.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.squareup.okhttp3:okhttp:5.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.squareup.okio:okio-bom:3.0.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.squareup.okio:okio-fakefilesystem-jvm:3.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.squareup.okio:okio-fakefilesystem:3.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.squareup.okio:okio-jvm:3.16.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.squareup.okio:okio:3.16.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.squareup.okio:okio-jvm:3.17.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.squareup.okio:okio:3.17.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.squareup.wire:wire-compiler:4.5.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.squareup.wire:wire-grpc-server-generator:4.5.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.squareup.wire:wire-grpc-server:4.5.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
@@ -221,7 +225,8 @@ com.sun.istack:istack-commons-tools:4.1.2=jaxb
|
||||
com.sun.xml.bind.external:relaxng-datatype:4.0.9=jaxb
|
||||
com.sun.xml.bind.external:rngom:4.0.9=jaxb
|
||||
com.sun.xml.dtd-parser:dtd-parser:1.5.1=jaxb
|
||||
com.zaxxer:HikariCP:7.0.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.zaxxer:HikariCP:7.0.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.zaxxer:HikariCP:7.1.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
commons-beanutils:commons-beanutils:1.10.1=checkstyle
|
||||
commons-codec:commons-codec:1.15=checkstyle
|
||||
commons-codec:commons-codec:1.19.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -246,79 +251,74 @@ io.github.ss-bhatt:testcontainers-valkey:1.0.0=testCompileClasspath,testRuntimeC
|
||||
io.grpc:grpc-alts:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-api:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-auth:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-census:1.76.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-census:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-context:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-core:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-googleapis:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-grpclb:1.76.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.grpc:grpc-grpclb:1.81.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-grpclb:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-inprocess:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-netty-shaded:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-netty:1.76.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-opentelemetry:1.76.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.grpc:grpc-opentelemetry:1.81.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-netty:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-opentelemetry:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-protobuf-lite:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-protobuf:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-rls:1.76.3=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.grpc:grpc-rls:1.81.0=testRuntimeClasspath
|
||||
io.grpc:grpc-services:1.76.3=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
io.grpc:grpc-services:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-rls:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-services:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-stub:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-util:1.76.3=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
io.grpc:grpc-util:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-xds:1.76.3=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
io.grpc:grpc-xds:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.netty:netty-buffer:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-codec-http2:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-codec-http:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-codec-socks:4.1.124.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.netty:netty-codec:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-common:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-handler-proxy:4.1.124.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.netty:netty-handler:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-resolver:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-testing:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-util:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-xds:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-buffer:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-codec-http2:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-codec-http:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-codec-socks:4.1.132.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.netty:netty-codec:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-common:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-handler-proxy:4.1.132.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.netty:netty-handler:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-resolver:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-tcnative-boringssl-static:2.0.52.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-tcnative-classes:2.0.52.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-transport-native-unix-common:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-transport:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-transport-native-unix-common:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-transport:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-api:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-contrib-exemplar-util:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-contrib-grpc-metrics:0.31.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
io.opencensus:opencensus-contrib-grpc-metrics:0.31.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-contrib-exemplar-util:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-contrib-grpc-metrics:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-contrib-grpc-util:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-contrib-http-util:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-contrib-resource-util:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-exporter-metrics-util:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-exporter-stats-stackdriver:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-impl-core:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-impl:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-contrib-resource-util:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-exporter-metrics-util:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-exporter-stats-stackdriver:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-impl-core:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opencensus:opencensus-impl:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry.contrib:opentelemetry-gcp-resources:1.37.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.opentelemetry.contrib:opentelemetry-gcp-resources:1.45.0-alpha=testCompileClasspath
|
||||
io.opentelemetry.instrumentation:opentelemetry-grpc-1.6:2.1.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator:2.1.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry.semconv:opentelemetry-semconv:1.29.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-api:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-api:1.62.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-api:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-api:1.64.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-bom:1.42.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-common:1.62.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-context:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-context:1.62.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-exporter-logging:1.62.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-common:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-common:1.64.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-context:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-context:1.64.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-exporter-logging:1.64.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-extension-incubator:1.35.0-alpha=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-common:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-common:1.62.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.62.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.62.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-logs:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-logs:1.62.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-metrics:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-metrics:1.62.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-trace:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-trace:1.62.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk:1.62.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-common:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-common:1.64.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.64.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.64.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-logs:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-logs:1.64.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-metrics:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-metrics:1.64.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-trace:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-trace:1.64.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk:1.64.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.outfoxx:swiftpoet:1.3.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.perfmark:perfmark-api:0.27.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.protostuff:protostuff-api:1.8.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -346,9 +346,9 @@ junit:junit:4.13.2=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileCl
|
||||
net.arnx:nashorn-promise:0.1.1=testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy-agent:1.17.7=testCompileClasspath,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.17.7=compileClasspath,nonprodCompileClasspath
|
||||
net.bytebuddy:byte-buddy:1.18.8=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.18.8-jdk5=testCompileClasspath
|
||||
net.java.dev.jna:jna:5.13.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.18.11=testCompileClasspath,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.18.8=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
|
||||
net.java.dev.jna:jna:5.13.0=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
net.ltgt.gradle.incap:incap:0.2=annotationProcessor,testAnnotationProcessor
|
||||
net.sf.saxon:Saxon-HE:12.5=checkstyle
|
||||
org.abego.treelayout:org.abego.treelayout.core:1.0.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -359,25 +359,25 @@ org.antlr:antlr4:4.13.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonp
|
||||
org.apache.arrow:arrow-format:17.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.arrow:arrow-memory-core:17.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.arrow:arrow-vector:17.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.avro:avro:1.11.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-model-fn-execution:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-model-job-management:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-model-pipeline:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.avro:avro:1.12.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-model-fn-execution:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-model-job-management:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-model-pipeline:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-core-construction-java:2.54.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-core-java:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-direct-java:2.73.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-google-cloud-dataflow-java:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-java-fn-execution:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-core:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-expansion-service:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-extensions-arrow:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-extensions-avro:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-extensions-google-cloud-platform-core:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-extensions-protobuf:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-core-java:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-direct-java:2.75.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-google-cloud-dataflow-java:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-java-fn-execution:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-core:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-expansion-service:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-extensions-arrow:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-extensions-avro:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-extensions-google-cloud-platform-core:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-extensions-protobuf:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-fn-execution:2.54.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-harness:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-io-google-cloud-platform:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-transform-service-launcher:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-harness:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-io-google-cloud-platform:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-transform-service-launcher:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-vendor-grpc-1_60_1:0.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-vendor-grpc-1_69_0:0.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-vendor-guava-32_1_2-jre:0.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -404,22 +404,23 @@ org.apache.maven.doxia:doxia-logging-api:1.12.0=checkstyle
|
||||
org.apache.maven.doxia:doxia-module-xdoc:1.12.0=checkstyle
|
||||
org.apache.maven.doxia:doxia-sink-api:1.12.0=checkstyle
|
||||
org.apache.mina:mina-core:2.2.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-common:3.0.0-M4=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-core:3.0.0-M4=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-scp:3.0.0-M4=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-sftp:3.0.0-M4=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.tomcat:tomcat-annotations-api:11.0.22=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-common:3.0.0-M5=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-core:3.0.0-M5=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-scp:3.0.0-M5=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-sftp:3.0.0-M5=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.tomcat:tomcat-annotations-api:11.0.24=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.xbean:xbean-reflect:3.7=checkstyle
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
|
||||
org.bouncycastle:bcpg-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.bouncycastle:bcpkix-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.bouncycastle:bcprov-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.bouncycastle:bcutil-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.bouncycastle:bcpg-jdk18on:1.85=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.bouncycastle:bcpkix-jdk18on:1.85=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.bouncycastle:bcprov-jdk18on:1.85=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.bouncycastle:bcutil-jdk18on:1.85=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.checkerframework:checker-compat-qual:2.5.3=annotationProcessor,compileClasspath,nonprodCompileClasspath,testAnnotationProcessor,testCompileClasspath
|
||||
org.checkerframework:checker-compat-qual:2.5.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.checkerframework:checker-qual:3.19.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
org.checkerframework:checker-qual:3.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.checkerframework:checker-qual:3.49.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
org.checkerframework:checker-qual:3.49.3=checkstyle
|
||||
org.checkerframework:checker-qual:3.55.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.27=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.codehaus.plexus:plexus-classworlds:2.6.0=checkstyle
|
||||
org.codehaus.plexus:plexus-component-annotations:2.1.0=checkstyle
|
||||
@@ -430,18 +431,18 @@ org.conscrypt:conscrypt-openjdk-uber:2.5.2=compileClasspath,deploy_jar,nonprodCo
|
||||
org.eclipse.angus:angus-activation:2.0.3=jaxb
|
||||
org.eclipse.angus:angus-activation:2.1.0-M1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.eclipse.angus:jakarta.mail:2.1.0-M1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty.ee10:jetty-ee10-servlet:12.1.9=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty.ee10:jetty-ee10-webapp:12.1.9=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty.ee:jetty-ee-webapp:12.1.9=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-http:12.1.9=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-io:12.1.9=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-security:12.1.9=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-server:12.1.9=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-session:12.1.9=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-util:12.1.9=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-xml:12.1.9=testCompileClasspath,testRuntimeClasspath
|
||||
org.flywaydb:flyway-core:12.7.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.flywaydb:flyway-database-postgresql:12.7.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty.ee10:jetty-ee10-servlet:12.1.11=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty.ee10:jetty-ee10-webapp:12.1.11=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty.ee:jetty-ee-webapp:12.1.11=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-http:12.1.11=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-io:12.1.11=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-security:12.1.11=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-server:12.1.11=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-session:12.1.11=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-util:12.1.11=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-xml:12.1.11=testCompileClasspath,testRuntimeClasspath
|
||||
org.flywaydb:flyway-core:12.11.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.flywaydb:flyway-database-postgresql:12.11.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.freemarker:freemarker:2.3.34=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.glassfish.jaxb:codemodel:4.0.9=jaxb
|
||||
org.glassfish.jaxb:jaxb-core:4.0.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
@@ -469,7 +470,7 @@ org.javassist:javassist:3.28.0-GA=checkstyle
|
||||
org.jboss.logging:jboss-logging:3.6.3.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.jcommander:jcommander:3.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-bom:1.4.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-metadata-jvm:2.2.20=annotationProcessor,testAnnotationProcessor
|
||||
org.jetbrains.kotlin:kotlin-metadata-jvm:2.3.21=annotationProcessor,testAnnotationProcessor
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.6.10=annotationProcessor,testAnnotationProcessor
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.9.20=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-common:2.2.21=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -477,35 +478,35 @@ org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0=annotationProcessor,testAnnotation
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0=annotationProcessor,testAnnotationProcessor
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib:2.2.20=annotationProcessor,testAnnotationProcessor
|
||||
org.jetbrains.kotlin:kotlin-stdlib:2.2.21=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib:2.3.21=annotationProcessor,testAnnotationProcessor
|
||||
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlinx:kotlinx-datetime:0.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.0.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains:annotations:13.0=annotationProcessor,testAnnotationProcessor
|
||||
org.jetbrains:annotations:17.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jline:jline:4.1.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jetbrains:annotations:13.0=annotationProcessor,compileClasspath,deploy_jar,runtimeClasspath,testAnnotationProcessor
|
||||
org.jetbrains:annotations:17.0.0=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jline:jline:4.3.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.joda:joda-money:2.0.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.json:json:20260522=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jsoup:jsoup:1.22.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jspecify:jspecify:1.0.0=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
org.junit-pioneer:junit-pioneer:2.3.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-api:6.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-engine:6.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-migrationsupport:6.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-params:6.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-commons:6.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-engine:6.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-launcher:6.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-api:6.1.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-engine:6.1.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-migrationsupport:6.1.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-params:6.1.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-commons:6.1.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-engine:6.1.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-launcher:6.1.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-runner:1.14.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-suite-api:6.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-suite-api:6.1.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-suite-commons:1.14.4=testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-suite-engine:6.1.0=testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-suite:6.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit:junit-bom:6.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-suite-engine:6.1.2=testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-suite:6.1.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit:junit-bom:6.1.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.mockito:mockito-core:5.23.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.mockito:mockito-junit-jupiter:5.23.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.objenesis:objenesis:3.3=testRuntimeClasspath
|
||||
@@ -521,37 +522,38 @@ org.ow2.asm:asm:9.7.1=compileClasspath,nonprodCompileClasspath
|
||||
org.ow2.asm:asm:9.8=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.ow2.asm:asm:9.9=jacocoAnt
|
||||
org.pcollections:pcollections:4.0.1=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
org.postgresql:postgresql:42.7.11=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.postgresql:postgresql:42.7.13=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.reflections:reflections:0.10.2=checkstyle
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-api:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-chrome-driver:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-chromium-driver:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v146:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v147:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v148:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-edge-driver:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-firefox-driver:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-http:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-ie-driver:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-java:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-json:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-manager:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-os:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-remote-driver:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-safari-driver:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-support:4.44.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-api:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-chrome-driver:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-chromium-driver:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-latest:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v148:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v149:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v150:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-edge-driver:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-firefox-driver:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-http:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-ie-driver:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-java:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-json:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-manager:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-os:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-remote-driver:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-safari-driver:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-support:4.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.slf4j:jcl-over-slf4j:1.7.36=testCompileClasspath,testRuntimeClasspath
|
||||
org.slf4j:jul-to-slf4j:1.7.30=testRuntimeClasspath
|
||||
org.slf4j:slf4j-api:2.0.18=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.slf4j:slf4j-jdk14:2.0.17=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.snakeyaml:snakeyaml-engine:2.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.testcontainers:database-commons:1.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:jdbc:1.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:database-commons:1.21.4=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:jdbc:1.21.4=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:junit-jupiter:1.21.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:postgresql:1.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:postgresql:1.21.4=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:selenium:1.21.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:testcontainers:1.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:testcontainers:1.21.4=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.threeten:threetenbp:1.7.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.webjars.npm:viz.js-graphviz-java:2.1.3=testRuntimeClasspath
|
||||
org.xerial.snappy:snappy-java:1.1.10.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -559,16 +561,16 @@ org.xmlresolver:xmlresolver:5.2.2=checkstyle
|
||||
org.yaml:snakeyaml:2.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
redis.clients.authentication:redis-authx-core:0.1.1-beta2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
redis.clients:jedis:8.0.0-beta1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
tools.jackson.core:jackson-core:3.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
tools.jackson.core:jackson-databind:3.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
tools.jackson:jackson-bom:3.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
tools.jackson.core:jackson-core:3.1.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
tools.jackson.core:jackson-databind:3.1.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
tools.jackson:jackson-bom:3.1.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-api:17.1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-diagram:17.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-operations:17.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-postgresql:17.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-text:17.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-diagram:17.12.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-operations:17.12.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-postgresql:17.12.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-text:17.12.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-tools:17.1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-utility:17.1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler:17.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler:17.12.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
xerces:xmlParserAPIs:2.6.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
empty=devtool,shadow
|
||||
|
||||
@@ -38,6 +38,7 @@ import java.security.GeneralSecurityException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
@@ -106,12 +107,16 @@ public final class CacheModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
public static HostCache provideHostCache(
|
||||
Optional<SimplifiedJedisClient> jedisClient, CacheMetrics cacheMetrics) {
|
||||
Optional<SimplifiedJedisClient> jedisClient, Clock clock, CacheMetrics cacheMetrics) {
|
||||
if (jedisClient.isEmpty()) {
|
||||
return repoId ->
|
||||
Optional.ofNullable(EppResource.loadByCache(VKey.create(Host.class, repoId)));
|
||||
return repoId -> {
|
||||
Instant now = clock.now();
|
||||
return Optional.ofNullable(EppResource.loadByCache(VKey.create(Host.class, repoId)))
|
||||
.filter(host -> now.isBefore(host.getDeletionTime()))
|
||||
.map(host -> (Host) host.cloneProjectedAtTime(now));
|
||||
};
|
||||
}
|
||||
return new MultilayerHostCache(jedisClient.get(), cacheMetrics);
|
||||
return new MultilayerHostCache(jedisClient.get(), clock, cacheMetrics);
|
||||
}
|
||||
|
||||
private static SSLSocketFactory createValkeySslSocketFactory(String valkeyCertificateAuthority) {
|
||||
|
||||
@@ -19,7 +19,6 @@ import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.util.Clock;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@@ -30,12 +29,9 @@ import java.util.Optional;
|
||||
public class MultilayerDomainCache extends MultilayerEppResourceCache<Domain>
|
||||
implements DomainCache {
|
||||
|
||||
private final Clock clock;
|
||||
|
||||
public MultilayerDomainCache(
|
||||
SimplifiedJedisClient jedisClient, Clock clock, CacheMetrics cacheMetrics) {
|
||||
super(jedisClient, cacheMetrics);
|
||||
this.clock = clock;
|
||||
super(jedisClient, clock, cacheMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,15 +42,10 @@ public class MultilayerDomainCache extends MultilayerEppResourceCache<Domain>
|
||||
@Override
|
||||
protected Optional<Domain> loadFromDatabase(String domainName) {
|
||||
// Don't use the cache (avoid caching the same domain twice). Do use the replica SQL instance.
|
||||
Optional<Domain> possibleDomain =
|
||||
Optional.ofNullable(
|
||||
ForeignKeyUtils.loadMostRecentResourceObjects(
|
||||
Domain.class, ImmutableList.of(domainName), true)
|
||||
.get(domainName));
|
||||
Instant now = clock.now();
|
||||
return possibleDomain
|
||||
.filter(domain -> now.isBefore(domain.getDeletionTime()))
|
||||
.map(domain -> domain.cloneProjectedAtTime(now));
|
||||
return Optional.ofNullable(
|
||||
ForeignKeyUtils.loadMostRecentResourceObjects(
|
||||
Domain.class, ImmutableList.of(domainName), true)
|
||||
.get(domainName));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,7 +18,9 @@ import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.util.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@@ -36,11 +38,13 @@ public abstract class MultilayerEppResourceCache<V extends EppResource> {
|
||||
.build();
|
||||
|
||||
private final SimplifiedJedisClient jedisClient;
|
||||
private final Clock clock;
|
||||
private final CacheMetrics cacheMetrics;
|
||||
|
||||
protected MultilayerEppResourceCache(
|
||||
SimplifiedJedisClient jedisClient, CacheMetrics cacheMetrics) {
|
||||
SimplifiedJedisClient jedisClient, Clock clock, CacheMetrics cacheMetrics) {
|
||||
this.jedisClient = jedisClient;
|
||||
this.clock = clock;
|
||||
this.cacheMetrics = cacheMetrics;
|
||||
}
|
||||
|
||||
@@ -50,7 +54,16 @@ public abstract class MultilayerEppResourceCache<V extends EppResource> {
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Optional<V> loadFromCaches(Class<V> clazz, String key) {
|
||||
Instant now = clock.now();
|
||||
return (Optional<V>)
|
||||
loadFromCachesInternal(clazz, key)
|
||||
.filter(v -> now.isBefore(v.getDeletionTime()))
|
||||
.map(v -> v.cloneProjectedAtTime(now));
|
||||
}
|
||||
|
||||
private Optional<V> loadFromCachesInternal(Class<V> clazz, String key) {
|
||||
// hopefully the resource is in the local cache
|
||||
Optional<V> possibleValue = Optional.ofNullable(localCache.getIfPresent(key));
|
||||
if (possibleValue.isPresent()) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.Clock;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@@ -27,8 +28,9 @@ import java.util.Optional;
|
||||
*/
|
||||
public class MultilayerHostCache extends MultilayerEppResourceCache<Host> implements HostCache {
|
||||
|
||||
public MultilayerHostCache(SimplifiedJedisClient jedisClient, CacheMetrics cacheMetrics) {
|
||||
super(jedisClient, cacheMetrics);
|
||||
public MultilayerHostCache(
|
||||
SimplifiedJedisClient jedisClient, Clock clock, CacheMetrics cacheMetrics) {
|
||||
super(jedisClient, clock, cacheMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.config;
|
||||
|
||||
import static com.google.common.base.Suppliers.memoize;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap;
|
||||
import static google.registry.config.ConfigUtils.makeUrl;
|
||||
import static google.registry.util.DateTimeUtils.START_INSTANT;
|
||||
@@ -49,6 +50,7 @@ import jakarta.mail.internet.AddressException;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.time.DayOfWeek;
|
||||
@@ -59,6 +61,7 @@ import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
/**
|
||||
* Central clearing-house for all configuration.
|
||||
@@ -1110,6 +1113,40 @@ public final class RegistryConfig {
|
||||
return config.registryPolicy.registryAdminClientId;
|
||||
}
|
||||
|
||||
/** Returns the total length of the Expiry Access Period. */
|
||||
@Provides
|
||||
@Config("domainExpiryAccessPeriodTotalLength")
|
||||
public static Duration provideDomainExpiryAccessPeriodTotalLength(
|
||||
RegistryConfigSettings config) {
|
||||
return Duration.ofSeconds(config.registryPolicy.domainExpiryAccessPeriod.totalLengthSeconds);
|
||||
}
|
||||
|
||||
/** Returns the length of each tier of the Expiry Access Period. */
|
||||
@Provides
|
||||
@Config("domainExpiryAccessPeriodTierLength")
|
||||
public static Duration provideDomainExpiryAccessPeriodTierLength(
|
||||
RegistryConfigSettings config) {
|
||||
return Duration.ofSeconds(config.registryPolicy.domainExpiryAccessPeriod.tierLengthSeconds);
|
||||
}
|
||||
|
||||
/** Returns a map of the fee for the first tier of the Expiry Access Period, by currency. */
|
||||
@Provides
|
||||
@Config("domainExpiryAccessPeriodInitialFee")
|
||||
public static ImmutableMap<CurrencyUnit, BigDecimal> provideDomainExpiryAccessPeriodInitialFee(
|
||||
RegistryConfigSettings config) {
|
||||
return config.registryPolicy.domainExpiryAccessPeriod.initialFee.entrySet().stream()
|
||||
.collect(toImmutableMap(entry -> CurrencyUnit.of(entry.getKey()), Entry::getValue));
|
||||
}
|
||||
|
||||
/** Returns a map of the fee for the last tier of the Expiry Access Period, by currency. */
|
||||
@Provides
|
||||
@Config("domainExpiryAccessPeriodFinalFee")
|
||||
public static ImmutableMap<CurrencyUnit, BigDecimal> provideDomainExpiryAccessPeriodFinalFee(
|
||||
RegistryConfigSettings config) {
|
||||
return config.registryPolicy.domainExpiryAccessPeriod.finalFee.entrySet().stream()
|
||||
.collect(toImmutableMap(entry -> CurrencyUnit.of(entry.getKey()), Entry::getValue));
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
static RegistryConfigSettings provideRegistryConfigSettings() {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.config;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -94,6 +95,7 @@ public class RegistryConfigSettings {
|
||||
public String tmchCrlUrl;
|
||||
public String tmchMarksDbUrl;
|
||||
public String registryAdminClientId;
|
||||
public DomainExpiryAccessPeriod domainExpiryAccessPeriod;
|
||||
public String premiumTermsExportDisclaimer;
|
||||
public String reservedTermsExportDisclaimer;
|
||||
public String rdapTos;
|
||||
@@ -106,6 +108,13 @@ public class RegistryConfigSettings {
|
||||
public Set<String> noPollMessageOnDeletionRegistrarIds;
|
||||
}
|
||||
|
||||
public static class DomainExpiryAccessPeriod {
|
||||
public long totalLengthSeconds;
|
||||
public long tierLengthSeconds;
|
||||
public Map<String, BigDecimal> initialFee;
|
||||
public Map<String, BigDecimal> finalFee;
|
||||
}
|
||||
|
||||
/** Configuration for Hibernate. */
|
||||
public static class Hibernate {
|
||||
public boolean allowNestedTransactions;
|
||||
|
||||
@@ -88,6 +88,28 @@ registryPolicy:
|
||||
# registrar
|
||||
registryAdminClientId: TheRegistrar
|
||||
|
||||
# Configuration for the Expiry Access Period (XAP) that occurs immediately
|
||||
# following completion of a domain's deletion. This needs to be enabled on a
|
||||
# per-TLD basis as well.
|
||||
domainExpiryAccessPeriod:
|
||||
# The length of the total expiry access period; defaults to 10 days.
|
||||
totalLengthSeconds: 864000
|
||||
# The length of each tier during the expiry access period; defaults to 1
|
||||
# hour. The XAP fee remains constant for the duration of each tier. The
|
||||
# total length must be evenly divisible by the tier length, i.e. there
|
||||
# should be a whole number of tiers all the same length.
|
||||
tierLengthSeconds: 3600
|
||||
# The initial fee for the first tier when the expiry access period starts,
|
||||
# specified for each currency this registry platform uses.
|
||||
initialFee:
|
||||
USD: 100000
|
||||
JPY: 10000000
|
||||
# The final fee for the last tier when the expiry access period ends,
|
||||
# specified for each currency this registry platform uses.
|
||||
finalFee:
|
||||
USD: 10
|
||||
JPY: 1000
|
||||
|
||||
# Disclaimer at the top of the exported premium terms list.
|
||||
premiumTermsExportDisclaimer: |
|
||||
This list contains domains for the TLD offered at a premium price. This
|
||||
|
||||
+10
@@ -323,4 +323,14 @@
|
||||
<schedule>*/5 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/syncRemoteCache]]></url>
|
||||
<name>syncRemoteCache</name>
|
||||
<description>
|
||||
Syncs remote (Valkey/Redis) EPP resource caches with changes made recently.
|
||||
</description>
|
||||
<!-- Runs every 5 minutes. RDAP needs to be updated within 60 minutes of changes. -->
|
||||
<schedule>*/5 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
</entries>
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
package google.registry.flows;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.toLogSafeLabel;
|
||||
import static java.util.Collections.EMPTY_LIST;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -90,9 +90,7 @@ public class FlowReporter {
|
||||
*/
|
||||
private static Optional<String> extractTld(String domainName) {
|
||||
int index = domainName.indexOf('.');
|
||||
return index == -1
|
||||
? Optional.empty()
|
||||
: Optional.of(Ascii.toLowerCase(domainName.substring(index + 1)));
|
||||
return index == -1 ? Optional.empty() : toLogSafeLabel(domainName.substring(index + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,6 +46,7 @@ import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@@ -100,8 +101,30 @@ public final class ResourceFlowUtils {
|
||||
|
||||
public static <R extends EppResource> void verifyResourceDoesNotExist(
|
||||
Class<R> clazz, String targetId, Instant now, String registrarId) throws EppException {
|
||||
Optional<R> resource = ForeignKeyUtils.loadResource(clazz, targetId, now);
|
||||
verifyResourceDoesNotExist(clazz, targetId, now, Duration.ZERO, registrarId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a resource with the specified type and id did not exist at the specified time.
|
||||
*
|
||||
* <p>This will throw an exception if the resource exists with a deletionTime greater than the
|
||||
* specified time (i.e. a registrar is attempting to create a duplicate domain, host, or contact).
|
||||
* If the resource had existed within the specified lookback window, but is not active now, i.e.
|
||||
* it is recently deleted, then don't throw an exception, but do return the recently deleted
|
||||
* resource for further processing (this is used by the Expiry Access Period).
|
||||
*
|
||||
* <p>If there is no need to specially handle the situation of recently deleted resources, then
|
||||
* simply pass a lookback duration of {@link Duration#ZERO}.
|
||||
*/
|
||||
public static <R extends EppResource> Optional<R> verifyResourceDoesNotExist(
|
||||
Class<R> clazz, String targetId, Instant now, Duration lookback, String registrarId)
|
||||
throws EppException {
|
||||
Instant startOfWindow = now.minus(lookback);
|
||||
Optional<R> resource = ForeignKeyUtils.loadResource(clazz, targetId, startOfWindow);
|
||||
if (resource.isPresent()) {
|
||||
if (resource.get().getDeletionTime().isBefore(now)) {
|
||||
return resource;
|
||||
}
|
||||
// These are similar exceptions, but we can track them internally as log-based metrics.
|
||||
if (Objects.equals(registrarId, resource.get().getPersistedCurrentSponsorRegistrarId())) {
|
||||
throw new ResourceAlreadyExistsForThisClientException(targetId);
|
||||
@@ -109,6 +132,7 @@ public final class ResourceFlowUtils {
|
||||
throw new ResourceCreateContentionException(targetId);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/** Check that the given AuthInfo is present for a resource being transferred. */
|
||||
|
||||
@@ -25,6 +25,7 @@ import static google.registry.flows.domain.DomainFlowUtils.checkHasBillingAccoun
|
||||
import static google.registry.flows.domain.DomainFlowUtils.getReservationTypes;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.isAnchorTenant;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.isDomainEligibleForXap;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.isRegisterBsaCreate;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.isReserved;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.isValidReservedCreate;
|
||||
@@ -75,6 +76,7 @@ import google.registry.model.eppoutput.CheckData.DomainCheck;
|
||||
import google.registry.model.eppoutput.CheckData.DomainCheckData;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
@@ -82,6 +84,7 @@ import google.registry.model.tld.label.ReservationType;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
@@ -138,6 +141,10 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||
@Config("maxChecks")
|
||||
int maxChecks;
|
||||
|
||||
@Inject
|
||||
@Config("domainExpiryAccessPeriodTotalLength")
|
||||
Duration domainExpiryAccessPeriodTotalLength;
|
||||
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject Clock clock;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@@ -249,7 +256,14 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||
return Optional.of(e.getMessage());
|
||||
}
|
||||
return getMessageForCheckWithToken(
|
||||
idn, existingDomains, bsaBlockedDomainNames, tldStates, token);
|
||||
idn, existingDomains, bsaBlockedDomainNames, tldStates, token, now);
|
||||
}
|
||||
|
||||
private static Optional<Domain> loadDomainIfInXap(
|
||||
String domainName, Instant now, Duration domainExpiryAccessPeriodTotalLength) {
|
||||
return ForeignKeyUtils.loadResource(
|
||||
Domain.class, domainName, now.minus(domainExpiryAccessPeriodTotalLength))
|
||||
.filter(domain -> isDomainEligibleForXap(domain, Tld.get(domain.getTld()), now));
|
||||
}
|
||||
|
||||
private Optional<String> getMessageForCheckWithToken(
|
||||
@@ -257,10 +271,18 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||
ImmutableMap<String, VKey<Domain>> existingDomains,
|
||||
ImmutableSet<InternetDomainName> bsaBlockedDomains,
|
||||
ImmutableMap<String, TldState> tldStates,
|
||||
Optional<AllocationToken> allocationToken) {
|
||||
Optional<AllocationToken> allocationToken,
|
||||
Instant now) {
|
||||
if (existingDomains.containsKey(domainName.toString())) {
|
||||
return Optional.of("In use");
|
||||
}
|
||||
Tld tld = Tld.get(domainName.parent().toString());
|
||||
if (tld.getExpiryAccessPeriodModeAt(now) == Tld.ExpiryAccessPeriodMode.ENABLED
|
||||
&& !Registrar.loadByRegistrarIdCached(registrarId).get().getExpiryAccessPeriodEnabled()
|
||||
&& loadDomainIfInXap(domainName.toString(), now, domainExpiryAccessPeriodTotalLength)
|
||||
.isPresent()) {
|
||||
return Optional.of("Reserved");
|
||||
}
|
||||
TldState tldState = tldStates.get(domainName.parent().toString());
|
||||
if (isReserved(domainName, START_DATE_SUNRISE.equals(tldState))) {
|
||||
if (!isValidReservedCreate(domainName, allocationToken)
|
||||
@@ -339,16 +361,27 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||
.build());
|
||||
continue;
|
||||
}
|
||||
Optional<Domain> domainForFee = domain;
|
||||
boolean isAvailable = availableDomains.contains(domainName);
|
||||
if (isAvailable
|
||||
&& feeCheckItem.getCommandName().equals(FeeQueryCommandExtensionItem.CommandName.CREATE)
|
||||
&& tld.getExpiryAccessPeriodModeAt(now) == Tld.ExpiryAccessPeriodMode.ENABLED) {
|
||||
Optional<Domain> recentlyDeletedDomain =
|
||||
loadDomainIfInXap(domainName, now, domainExpiryAccessPeriodTotalLength);
|
||||
if (recentlyDeletedDomain.isPresent()) {
|
||||
domainForFee = recentlyDeletedDomain;
|
||||
}
|
||||
}
|
||||
handleFeeRequest(
|
||||
feeCheckItem,
|
||||
builder,
|
||||
domainNames.get(domainName),
|
||||
domain,
|
||||
domainForFee,
|
||||
feeCheck.getCurrency(),
|
||||
now,
|
||||
pricingLogic,
|
||||
token,
|
||||
availableDomains.contains(domainName),
|
||||
isAvailable,
|
||||
recurrences.getOrDefault(domainName, null));
|
||||
// In the case of a registrar that is running a tiered pricing promotion, we issue two
|
||||
// responses for the CREATE fee check command: one (the default response) with the
|
||||
|
||||
@@ -18,6 +18,7 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.flows.FlowUtils.persistEntityChanges;
|
||||
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.COLLISION_MESSAGE;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.checkHasBillingAccount;
|
||||
@@ -25,6 +26,7 @@ import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReference
|
||||
import static google.registry.flows.domain.DomainFlowUtils.createFeeCreateResponse;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.getReservationTypes;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.isAnchorTenant;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.isDomainEligibleForXap;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.isReserved;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.isValidReservedCreate;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateCreateCommandContactsAndNameservers;
|
||||
@@ -58,6 +60,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.CommandUseErrorException;
|
||||
import google.registry.flows.EppException.ParameterValuePolicyErrorException;
|
||||
@@ -71,6 +74,7 @@ import google.registry.flows.custom.DomainCreateFlowCustomLogic;
|
||||
import google.registry.flows.custom.DomainCreateFlowCustomLogic.BeforeResponseParameters;
|
||||
import google.registry.flows.custom.DomainCreateFlowCustomLogic.BeforeResponseReturnData;
|
||||
import google.registry.flows.custom.EntityChanges;
|
||||
import google.registry.flows.domain.DomainFlowUtils.DomainReservedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
@@ -107,6 +111,7 @@ import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.poll.PollMessage.Autorenew;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
@@ -123,6 +128,7 @@ import jakarta.inject.Inject;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.Money;
|
||||
|
||||
/**
|
||||
* An EPP flow that creates a new domain resource.
|
||||
@@ -219,6 +225,10 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@Inject DomainDeletionTimeCache domainDeletionTimeCache;
|
||||
|
||||
@Inject
|
||||
@Config("domainExpiryAccessPeriodTotalLength")
|
||||
Duration domainExpiryAccessPeriodTotalLength;
|
||||
|
||||
@Inject
|
||||
DomainCreateFlow() {}
|
||||
|
||||
@@ -245,6 +255,13 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
InternetDomainName domainName = validateDomainName(command.getDomainName());
|
||||
String domainLabel = domainName.parts().getFirst();
|
||||
Tld tld = Tld.get(domainName.parent().toString());
|
||||
Duration lookback =
|
||||
tld.getExpiryAccessPeriodModeAt(now) == Tld.ExpiryAccessPeriodMode.ENABLED
|
||||
? domainExpiryAccessPeriodTotalLength
|
||||
: Duration.ZERO;
|
||||
Optional<Domain> recentlyDeletedDomain =
|
||||
verifyResourceDoesNotExist(Domain.class, targetId, now, lookback, registrarId)
|
||||
.filter(domain -> isDomainEligibleForXap(domain, tld, now));
|
||||
validateCreateCommandContactsAndNameservers(command, tld, domainName);
|
||||
TldState tldState = tld.getTldState(now);
|
||||
Optional<LaunchCreateExtension> launchCreate =
|
||||
@@ -280,6 +297,10 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
if (!isSuperuser) {
|
||||
checkAllowedAccessToTld(registrarId, tld.getTldStr());
|
||||
checkHasBillingAccount(registrarId, tld.getTldStr());
|
||||
if (recentlyDeletedDomain.isPresent()
|
||||
&& !Registrar.loadByRegistrarIdCached(registrarId).get().getExpiryAccessPeriodEnabled()) {
|
||||
throw new DomainReservedException(domainName.toString());
|
||||
}
|
||||
boolean isValidReservedCreate = isValidReservedCreate(domainName, allocationToken);
|
||||
ClaimsList claimsList = ClaimsListDao.get();
|
||||
verifyIsGaOrSpecialCase(
|
||||
@@ -327,7 +348,14 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
eppInput.getSingleExtension(FeeCreateCommandExtension.class);
|
||||
FeesAndCredits feesAndCredits =
|
||||
pricingLogic.getCreatePrice(
|
||||
tld, targetId, now, years, isAnchorTenant, isSunriseCreate, allocationToken);
|
||||
tld,
|
||||
targetId,
|
||||
now,
|
||||
recentlyDeletedDomain,
|
||||
years,
|
||||
isAnchorTenant,
|
||||
isSunriseCreate,
|
||||
allocationToken);
|
||||
validateFeeChallenge(feeCreate, feesAndCredits, defaultTokenUsed);
|
||||
Optional<SecDnsCreateExtension> secDnsCreate =
|
||||
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
|
||||
@@ -358,7 +386,15 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
entitiesToInsert.add(createBillingEvent, autorenewBillingEvent, autorenewPollMessage);
|
||||
// Bill for EAP cost, if any.
|
||||
if (!feesAndCredits.getEapCost().isZero()) {
|
||||
entitiesToInsert.add(createEapBillingEvent(feesAndCredits, createBillingEvent));
|
||||
entitiesToInsert.add(
|
||||
createAdditionalBillingEvent(
|
||||
Reason.FEE_EARLY_ACCESS, feesAndCredits.getEapCost(), createBillingEvent));
|
||||
}
|
||||
// Bill for XAP cost, if any.
|
||||
if (!feesAndCredits.getXapCost().isZero()) {
|
||||
entitiesToInsert.add(
|
||||
createAdditionalBillingEvent(
|
||||
Reason.FEE_EXPIRY_ACCESS, feesAndCredits.getXapCost(), createBillingEvent));
|
||||
}
|
||||
|
||||
ImmutableSet<ReservationType> reservationTypes = getReservationTypes(domainName);
|
||||
@@ -434,7 +470,14 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
FeesAndCredits responseFeesAndCredits =
|
||||
shouldShowDefaultPrice
|
||||
? pricingLogic.getCreatePrice(
|
||||
tld, targetId, now, years, isAnchorTenant, isSunriseCreate, Optional.empty())
|
||||
tld,
|
||||
targetId,
|
||||
now,
|
||||
recentlyDeletedDomain,
|
||||
years,
|
||||
isAnchorTenant,
|
||||
isSunriseCreate,
|
||||
Optional.empty())
|
||||
: feesAndCredits;
|
||||
|
||||
BeforeResponseReturnData responseData =
|
||||
@@ -656,14 +699,14 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
}
|
||||
}
|
||||
|
||||
private static BillingEvent createEapBillingEvent(
|
||||
FeesAndCredits feesAndCredits, BillingEvent createBillingEvent) {
|
||||
private static BillingEvent createAdditionalBillingEvent(
|
||||
Reason reason, Money cost, BillingEvent createBillingEvent) {
|
||||
return new BillingEvent.Builder()
|
||||
.setReason(Reason.FEE_EARLY_ACCESS)
|
||||
.setReason(reason)
|
||||
.setTargetId(createBillingEvent.getTargetId())
|
||||
.setRegistrarId(createBillingEvent.getRegistrarId())
|
||||
.setPeriodYears(1)
|
||||
.setCost(feesAndCredits.getEapCost())
|
||||
.setCost(cost)
|
||||
.setEventTime(createBillingEvent.getEventTime())
|
||||
.setBillingTime(createBillingEvent.getBillingTime())
|
||||
.setFlags(createBillingEvent.getFlags())
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.flows.domain;
|
||||
|
||||
import static com.google.common.base.Ascii.toLowerCase;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
@@ -175,6 +176,27 @@ public class DomainFlowUtils {
|
||||
/** Maximum number of characters in a domain label, from RFC 2181. */
|
||||
private static final int MAX_LABEL_SIZE = 63;
|
||||
|
||||
/// Given a tld name or domain label, returns a label that can be safely logged, or
|
||||
/// `Optional.empty()` if `label` is null or blank.
|
||||
///
|
||||
/// If `label` contains disallowed characters, they are replaced with '-'. If the resulting
|
||||
/// string has a valid length, it is returned as is; if not, the first {@link #MAX_LABEL_SIZE}
|
||||
/// chars plus a suffix of `...` is returned.
|
||||
///
|
||||
/// This method is mainly for use by `FlowReporter#recordToLogs()` to ensure that the logs
|
||||
/// can be safely and correctly queried by SQL queries. See b/534931586 for more information.
|
||||
public static Optional<String> toLogSafeLabel(String label) {
|
||||
if (label == null || label.isBlank()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
var newLabel = ALLOWED_CHARS.negate().replaceFrom(toLowerCase(label), '-');
|
||||
if (newLabel.length() <= MAX_LABEL_SIZE) {
|
||||
return Optional.of(newLabel);
|
||||
} else {
|
||||
return Optional.of(newLabel.substring(0, MAX_LABEL_SIZE) + "...");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parsed version of {@code name} if domain name label follows our naming rules and is
|
||||
* under one of the given allowed TLDs.
|
||||
@@ -642,6 +664,7 @@ public class DomainFlowUtils {
|
||||
tld,
|
||||
domainNameString,
|
||||
now,
|
||||
domain,
|
||||
years,
|
||||
isAnchorTenant(domainName, allocationToken, Optional.empty()),
|
||||
isSunrise,
|
||||
@@ -765,6 +788,9 @@ public class DomainFlowUtils {
|
||||
if (!feesAndCredits.getEapCost().isZero()) {
|
||||
throw new FeesRequiredDuringEarlyAccessProgramException(feesAndCredits.getEapCost());
|
||||
}
|
||||
if (!feesAndCredits.getXapCost().isZero()) {
|
||||
throw new FeesRequiredDuringExpiryAccessPeriodException(feesAndCredits.getXapCost());
|
||||
}
|
||||
if (feesAndCredits.getTotalCost().isZero() || !feesAndCredits.isFeeExtensionRequired()) {
|
||||
return;
|
||||
}
|
||||
@@ -1168,6 +1194,32 @@ public class DomainFlowUtils {
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the domain was deleted during its Add Grace Period (AGP).
|
||||
*
|
||||
* <p>Per policy, domains deleted during AGP are not subject to Expiry Access Period (XAP) fees or
|
||||
* reservation restrictions upon subsequent re-registration.
|
||||
*/
|
||||
public static boolean wasDeletedDuringAddGracePeriod(Domain domain, Tld tld) {
|
||||
if (domain.getCreationTime() == null || domain.getDeletionTime() == null) {
|
||||
return false;
|
||||
}
|
||||
return !domain
|
||||
.getDeletionTime()
|
||||
.isAfter(domain.getCreationTime().plus(tld.getAddGracePeriodLength()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the domain was deleted before {@code now} and is eligible for Expiry Access
|
||||
* Period (XAP) evaluation.
|
||||
*/
|
||||
public static boolean isDomainEligibleForXap(Domain domain, Tld tld, Instant now) {
|
||||
if (domain.getDeletionTime() == null || !domain.getDeletionTime().isBefore(now)) {
|
||||
return false;
|
||||
}
|
||||
return !wasDeletedDuringAddGracePeriod(domain, tld);
|
||||
}
|
||||
|
||||
/** Resource linked to this domain does not exist. */
|
||||
static class LinkedResourcesDoNotExistException extends ObjectDoesNotExistException {
|
||||
public LinkedResourcesDoNotExistException(Class<?> type, ImmutableSet<String> resourceIds) {
|
||||
@@ -1355,6 +1407,18 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** Fees must be explicitly acknowledged when creating domains during the Expiry Access Period. */
|
||||
static class FeesRequiredDuringExpiryAccessPeriodException
|
||||
extends RequiredParameterMissingException {
|
||||
|
||||
public FeesRequiredDuringExpiryAccessPeriodException(Money expectedFee) {
|
||||
super(
|
||||
"Fees must be explicitly acknowledged when creating domains "
|
||||
+ "during the Expiry Access Period. The XAP fee is: "
|
||||
+ expectedFee);
|
||||
}
|
||||
}
|
||||
|
||||
/** The 'grace-period', 'applied' and 'refundable' fields are disallowed by server policy. */
|
||||
static class UnsupportedFeeAttributeException extends UnimplementedOptionException {
|
||||
UnsupportedFeeAttributeException() {
|
||||
|
||||
@@ -15,13 +15,18 @@
|
||||
package google.registry.flows.domain;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.isDomainEligibleForXap;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.zeroInCurrency;
|
||||
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.discountTokenInvalidForPremiumName;
|
||||
import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Range;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.custom.DomainPricingCustomLogic;
|
||||
import google.registry.flows.custom.DomainPricingCustomLogic.CreatePriceParameters;
|
||||
@@ -31,6 +36,7 @@ import google.registry.flows.custom.DomainPricingCustomLogic.TransferPriceParame
|
||||
import google.registry.flows.custom.DomainPricingCustomLogic.UpdatePriceParameters;
|
||||
import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
|
||||
import google.registry.model.billing.BillingRecurrence;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.fee.BaseFee;
|
||||
import google.registry.model.domain.fee.BaseFee.FeeType;
|
||||
import google.registry.model.domain.fee.Fee;
|
||||
@@ -40,7 +46,9 @@ import google.registry.model.domain.token.AllocationToken.TokenBehavior;
|
||||
import google.registry.model.pricing.PremiumPricingEngine.DomainPrices;
|
||||
import google.registry.model.tld.Tld;
|
||||
import jakarta.inject.Inject;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -54,11 +62,28 @@ import org.joda.money.Money;
|
||||
*/
|
||||
public final class DomainPricingLogic {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private final DomainPricingCustomLogic customLogic;
|
||||
private final Duration expiryAccessPeriodTotalLength;
|
||||
private final Duration expiryAccessPeriodTierLength;
|
||||
private final ImmutableMap<CurrencyUnit, BigDecimal> expiryAccessPeriodInitialFee;
|
||||
private final ImmutableMap<CurrencyUnit, BigDecimal> expiryAccessPeriodFinalFee;
|
||||
|
||||
@Inject
|
||||
public DomainPricingLogic(DomainPricingCustomLogic customLogic) {
|
||||
public DomainPricingLogic(
|
||||
DomainPricingCustomLogic customLogic,
|
||||
@Config("domainExpiryAccessPeriodTotalLength") Duration expiryAccessPeriodTotalLength,
|
||||
@Config("domainExpiryAccessPeriodTierLength") Duration expiryAccessPeriodTierLength,
|
||||
@Config("domainExpiryAccessPeriodInitialFee")
|
||||
ImmutableMap<CurrencyUnit, BigDecimal> expiryAccessPeriodInitialFee,
|
||||
@Config("domainExpiryAccessPeriodFinalFee")
|
||||
ImmutableMap<CurrencyUnit, BigDecimal> expiryAccessPeriodFinalFee) {
|
||||
this.customLogic = customLogic;
|
||||
this.expiryAccessPeriodTotalLength = expiryAccessPeriodTotalLength;
|
||||
this.expiryAccessPeriodTierLength = expiryAccessPeriodTierLength;
|
||||
this.expiryAccessPeriodInitialFee = expiryAccessPeriodInitialFee;
|
||||
this.expiryAccessPeriodFinalFee = expiryAccessPeriodFinalFee;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,35 +96,23 @@ public final class DomainPricingLogic {
|
||||
Tld tld,
|
||||
String domainName,
|
||||
Instant dateTime,
|
||||
Optional<Domain> recentlyDeletedDomain,
|
||||
int years,
|
||||
boolean isAnchorTenant,
|
||||
boolean isSunriseCreate,
|
||||
Optional<AllocationToken> allocationToken)
|
||||
throws EppException {
|
||||
CurrencyUnit currency = tld.getCurrency();
|
||||
|
||||
BaseFee createFee;
|
||||
// Domain create cost is always zero for anchor tenants
|
||||
if (isAnchorTenant) {
|
||||
createFee = Fee.create(zeroInCurrency(currency), FeeType.CREATE, false);
|
||||
} else {
|
||||
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
|
||||
if (allocationToken.isPresent()) {
|
||||
// Handle any special NONPREMIUM / SPECIFIED cases configured in the token
|
||||
domainPrices =
|
||||
applyTokenToDomainPrices(domainPrices, tld, dateTime, years, allocationToken.get());
|
||||
}
|
||||
Money domainCreateCost =
|
||||
getDomainCreateCostWithDiscount(domainPrices, years, allocationToken, tld);
|
||||
// Apply a sunrise discount if configured and applicable
|
||||
if (isSunriseCreate) {
|
||||
domainCreateCost =
|
||||
domainCreateCost.multipliedBy(
|
||||
1.0d - RegistryConfig.getSunriseDomainCreateDiscount(), RoundingMode.HALF_EVEN);
|
||||
}
|
||||
createFee =
|
||||
Fee.create(domainCreateCost.getAmount(), FeeType.CREATE, domainPrices.isPremium());
|
||||
}
|
||||
Fee createFee =
|
||||
computeCreateFee(
|
||||
tld,
|
||||
domainName,
|
||||
dateTime,
|
||||
years,
|
||||
isAnchorTenant,
|
||||
isSunriseCreate,
|
||||
allocationToken,
|
||||
currency);
|
||||
|
||||
// Create fees for the cost and the EAP fee, if any.
|
||||
Fee eapFee = tld.getEapFeeFor(dateTime);
|
||||
@@ -109,6 +122,7 @@ public final class DomainPricingLogic {
|
||||
if (!isAnchorTenant && !eapFee.hasZeroCost()) {
|
||||
feesBuilder.addFeeOrCredit(eapFee);
|
||||
}
|
||||
maybeAddXapFee(tld, dateTime, recentlyDeletedDomain, currency, feesBuilder);
|
||||
|
||||
// Apply custom logic to the create fee, if any.
|
||||
return customLogic.customizeCreatePrice(
|
||||
@@ -121,6 +135,113 @@ public final class DomainPricingLogic {
|
||||
.build());
|
||||
}
|
||||
|
||||
private Fee computeCreateFee(
|
||||
Tld tld,
|
||||
String domainName,
|
||||
Instant dateTime,
|
||||
int years,
|
||||
boolean isAnchorTenant,
|
||||
boolean isSunriseCreate,
|
||||
Optional<AllocationToken> allocationToken,
|
||||
CurrencyUnit currency)
|
||||
throws EppException {
|
||||
// Domain create cost is always zero for anchor tenants
|
||||
if (isAnchorTenant) {
|
||||
return Fee.create(zeroInCurrency(currency), FeeType.CREATE, false);
|
||||
}
|
||||
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
|
||||
if (allocationToken.isPresent()) {
|
||||
// Handle any special NONPREMIUM / SPECIFIED cases configured in the token
|
||||
domainPrices =
|
||||
applyTokenToDomainPrices(domainPrices, tld, dateTime, years, allocationToken.get());
|
||||
}
|
||||
Money domainCreateCost =
|
||||
getDomainCreateCostWithDiscount(domainPrices, years, allocationToken, tld);
|
||||
// Apply a sunrise discount if configured and applicable
|
||||
if (isSunriseCreate) {
|
||||
domainCreateCost =
|
||||
domainCreateCost.multipliedBy(
|
||||
1.0d - RegistryConfig.getSunriseDomainCreateDiscount(), RoundingMode.HALF_EVEN);
|
||||
}
|
||||
return Fee.create(domainCreateCost.getAmount(), FeeType.CREATE, domainPrices.isPremium());
|
||||
}
|
||||
|
||||
private void maybeAddXapFee(
|
||||
Tld tld,
|
||||
Instant dateTime,
|
||||
Optional<Domain> recentlyDeletedDomain,
|
||||
CurrencyUnit currency,
|
||||
FeesAndCredits.Builder feesBuilder) {
|
||||
if (tld.getExpiryAccessPeriodModeAt(dateTime) == Tld.ExpiryAccessPeriodMode.ENABLED
|
||||
&& recentlyDeletedDomain.isPresent()
|
||||
&& isDomainEligibleForXap(recentlyDeletedDomain.get(), tld, dateTime)) {
|
||||
Optional<Fee> xapFee =
|
||||
getXapFeeFor(dateTime, recentlyDeletedDomain.get().getDeletionTime(), currency);
|
||||
xapFee.ifPresent(
|
||||
fee -> {
|
||||
feesBuilder.addFeeOrCredit(fee);
|
||||
if (!fee.hasZeroCost()) {
|
||||
feesBuilder.setFeeExtensionRequired(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates and returns the Expiry Access Fee for a recently deleted domain at the given time.
|
||||
*/
|
||||
Optional<Fee> getXapFeeFor(Instant dateTime, Instant deletionTime, CurrencyUnit currency) {
|
||||
Duration elapsedTimeInXap = Duration.between(deletionTime, dateTime);
|
||||
if (!expiryAccessPeriodInitialFee.containsKey(currency)
|
||||
|| !expiryAccessPeriodFinalFee.containsKey(currency)) {
|
||||
// If the XAP schedule hasn't been configured in YAML for the currency this TLD uses, log the
|
||||
// error and then short-circuit return (to allow the EPP flow to continue normally).
|
||||
logger.atSevere().log(
|
||||
"Expiry Access Period configuration is lacking initial or final fees for currency %s.",
|
||||
currency);
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
// Determine which tier the current time falls into (0-indexed).
|
||||
long tier = elapsedTimeInXap.toMillis() / expiryAccessPeriodTierLength.toMillis();
|
||||
long numTiers =
|
||||
expiryAccessPeriodTotalLength.toMillis() / expiryAccessPeriodTierLength.toMillis();
|
||||
if (tier < 0 || tier >= numTiers) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
// Calculate the parameters for the geometric sequence: XAP fee = initialFee * ratio ^ tier.
|
||||
BigDecimal xapFee;
|
||||
if (expiryAccessPeriodInitialFee.get(currency).signum() == 0 || numTiers <= 1 || tier == 0) {
|
||||
xapFee =
|
||||
expiryAccessPeriodInitialFee
|
||||
.get(currency)
|
||||
.setScale(currency.getDecimalPlaces(), RoundingMode.HALF_EVEN);
|
||||
} else {
|
||||
double base =
|
||||
expiryAccessPeriodFinalFee.get(currency).doubleValue()
|
||||
/ expiryAccessPeriodInitialFee.get(currency).doubleValue();
|
||||
double exponent = 1.0 / (numTiers - 1.0);
|
||||
BigDecimal ratio = BigDecimal.valueOf(Math.pow(base, exponent));
|
||||
BigDecimal fee = expiryAccessPeriodInitialFee.get(currency).multiply(ratio.pow((int) tier));
|
||||
xapFee = fee.setScale(currency.getDecimalPlaces(), RoundingMode.HALF_EVEN);
|
||||
}
|
||||
|
||||
Range<Instant> validPeriod =
|
||||
Range.closedOpen(
|
||||
deletionTime.plus(expiryAccessPeriodTierLength.multipliedBy(tier)),
|
||||
deletionTime.plus(expiryAccessPeriodTierLength.multipliedBy(tier + 1)));
|
||||
return Optional.of(
|
||||
Fee.create(
|
||||
xapFee,
|
||||
FeeType.XAP,
|
||||
// An XAP fee does not count as premium -- it's a separate one-time fee, independent of
|
||||
// which the domain is separately considered standard vs premium.
|
||||
false,
|
||||
validPeriod,
|
||||
validPeriod.upperEndpoint()));
|
||||
}
|
||||
|
||||
/** Returns a new renewal cost for the pricer. */
|
||||
public FeesAndCredits getRenewPrice(
|
||||
Tld tld,
|
||||
|
||||
@@ -77,6 +77,11 @@ public class FeesAndCredits extends ImmutableObject implements Buildable {
|
||||
return getTotalCostForType(FeeType.EAP);
|
||||
}
|
||||
|
||||
/** Returns the XAP cost for the event. */
|
||||
public Money getXapCost() {
|
||||
return getTotalCostForType(FeeType.XAP);
|
||||
}
|
||||
|
||||
/** Returns the renew cost for the event. */
|
||||
public Money getRenewCost() {
|
||||
return getTotalCostForType(FeeType.RENEW);
|
||||
|
||||
+15
-5
@@ -242,7 +242,14 @@ public class AllocationTokenFlowUtils {
|
||||
case CREATE ->
|
||||
pricingLogic
|
||||
.getCreatePrice(
|
||||
tld, domainName, now, yearsForAction, false, false, Optional.of(token))
|
||||
tld,
|
||||
domainName,
|
||||
now,
|
||||
Optional.empty(),
|
||||
yearsForAction,
|
||||
false,
|
||||
false,
|
||||
Optional.of(token))
|
||||
.getTotalCost()
|
||||
.getAmount();
|
||||
case RENEW ->
|
||||
@@ -270,11 +277,14 @@ public class AllocationTokenFlowUtils {
|
||||
return maybeTokenEntity.get();
|
||||
}
|
||||
|
||||
maybeTokenEntity = AllocationToken.get(VKey.create(AllocationToken.class, token));
|
||||
if (maybeTokenEntity.isEmpty()) {
|
||||
throw new NonexistentAllocationTokenException();
|
||||
VKey<AllocationToken> tokenKey = VKey.create(AllocationToken.class, token);
|
||||
AllocationToken tokenEntity =
|
||||
AllocationToken.get(tokenKey).orElseThrow(NonexistentAllocationTokenException::new);
|
||||
if (tokenEntity.getTokenType().equals(AllocationToken.TokenType.SINGLE_USE)) {
|
||||
// Reload the token to avoid possible cache race conditions where the token may have already
|
||||
// been redeemed
|
||||
tokenEntity = tm().loadByKey(tokenKey);
|
||||
}
|
||||
AllocationToken tokenEntity = maybeTokenEntity.get();
|
||||
validateTokenEntity(tokenEntity, registrarId, domainName, now);
|
||||
return tokenEntity;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import static google.registry.util.DateTimeUtils.parseInstant;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.KeyDeserializer;
|
||||
@@ -38,6 +39,7 @@ import com.google.common.collect.ImmutableSortedSet;
|
||||
import google.registry.model.common.FeatureFlag.FeatureStatus;
|
||||
import google.registry.model.common.TimedTransitionProperty;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.tld.Tld.ExpiryAccessPeriodMode;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.io.IOException;
|
||||
@@ -336,7 +338,8 @@ public class EntityYamlUtils {
|
||||
@Override
|
||||
public TimedTransitionProperty<TldState> deserialize(
|
||||
JsonParser jp, DeserializationContext context) throws IOException {
|
||||
SortedMap<String, String> valueMap = jp.readValueAs(SortedMap.class);
|
||||
SortedMap<String, String> valueMap =
|
||||
jp.readValueAs(new TypeReference<SortedMap<String, String>>() {});
|
||||
return TimedTransitionProperty.fromValueMap(
|
||||
valueMap.keySet().stream()
|
||||
.collect(
|
||||
@@ -347,6 +350,37 @@ public class EntityYamlUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom JSON deserializer for a {@link TimedTransitionProperty} of {@link
|
||||
* ExpiryAccessPeriodMode}.
|
||||
*/
|
||||
public static class TimedTransitionPropertyExpiryAccessPeriodModeDeserializer
|
||||
extends StdDeserializer<TimedTransitionProperty<ExpiryAccessPeriodMode>> {
|
||||
|
||||
public TimedTransitionPropertyExpiryAccessPeriodModeDeserializer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public TimedTransitionPropertyExpiryAccessPeriodModeDeserializer(
|
||||
Class<TimedTransitionProperty<ExpiryAccessPeriodMode>> t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimedTransitionProperty<ExpiryAccessPeriodMode> deserialize(
|
||||
JsonParser jp, DeserializationContext context) throws IOException {
|
||||
SortedMap<String, String> valueMap =
|
||||
jp.readValueAs(new TypeReference<SortedMap<String, String>>() {});
|
||||
return TimedTransitionProperty.fromValueMap(
|
||||
valueMap.keySet().stream()
|
||||
.collect(
|
||||
toImmutableSortedMap(
|
||||
natural(),
|
||||
key -> parseInstant(key),
|
||||
key -> ExpiryAccessPeriodMode.valueOf(valueMap.get(key)))));
|
||||
}
|
||||
}
|
||||
|
||||
/** A custom JSON deserializer for a {@link TimedTransitionProperty} of {@link Money}. */
|
||||
public static class TimedTransitionPropertyMoneyDeserializer
|
||||
extends StdDeserializer<TimedTransitionProperty<Money>> {
|
||||
@@ -362,7 +396,8 @@ public class EntityYamlUtils {
|
||||
@Override
|
||||
public TimedTransitionProperty<Money> deserialize(JsonParser jp, DeserializationContext context)
|
||||
throws IOException {
|
||||
SortedMap<String, LinkedHashMap<String, Object>> valueMap = jp.readValueAs(SortedMap.class);
|
||||
SortedMap<String, LinkedHashMap<String, Object>> valueMap =
|
||||
jp.readValueAs(new TypeReference<SortedMap<String, LinkedHashMap<String, Object>>>() {});
|
||||
return TimedTransitionProperty.fromValueMap(
|
||||
valueMap.keySet().stream()
|
||||
.collect(
|
||||
@@ -392,7 +427,8 @@ public class EntityYamlUtils {
|
||||
@Override
|
||||
public TimedTransitionProperty<FeatureStatus> deserialize(
|
||||
JsonParser jp, DeserializationContext context) throws IOException {
|
||||
SortedMap<String, String> valueMap = jp.readValueAs(SortedMap.class);
|
||||
SortedMap<String, String> valueMap =
|
||||
jp.readValueAs(new TypeReference<SortedMap<String, String>>() {});
|
||||
return TimedTransitionProperty.fromValueMap(
|
||||
valueMap.keySet().stream()
|
||||
.collect(
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.model;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.config.RegistryConfig.getEppResourceCachingDuration;
|
||||
import static google.registry.config.RegistryConfig.getEppResourceMaxCachedEntries;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
|
||||
@@ -400,4 +401,24 @@ public final class ForeignKeyUtils {
|
||||
.filter(e -> now.isBefore(e.getDeletionTime()))
|
||||
.map(e -> e.cloneProjectedAtTime(now));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the last created version of multiple {@link EppResource}s from the replica database by
|
||||
* foreign keys, using a cache.
|
||||
*
|
||||
* <p>This method ignores the config setting for caching, and is reserved for use cases that can
|
||||
* tolerate slightly stale data.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E extends EppResource> ImmutableMap<String, E> loadResourcesByCache(
|
||||
Class<E> clazz, Collection<String> foreignKeys, Instant now) {
|
||||
ImmutableSet<VKey<? extends EppResource>> vkeys =
|
||||
foreignKeys.stream().map(fk -> VKey.create(clazz, fk)).collect(toImmutableSet());
|
||||
return foreignKeyToResourceCache.getAll(vkeys).entrySet().stream()
|
||||
.filter(e -> e.getValue().isPresent() && now.isBefore(e.getValue().get().getDeletionTime()))
|
||||
.collect(
|
||||
toImmutableMap(
|
||||
e -> (String) e.getKey().getKey(),
|
||||
e -> (E) e.getValue().get().cloneProjectedAtTime(now)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ public abstract class BillingBase extends ImmutableObject
|
||||
@Deprecated // DO NOT USE THIS REASON. IT REMAINS BECAUSE OF HISTORICAL DATA. SEE b/31676071.
|
||||
ERROR(false),
|
||||
FEE_EARLY_ACCESS(true),
|
||||
FEE_EXPIRY_ACCESS(true),
|
||||
RENEW(true),
|
||||
RESTORE(true),
|
||||
SERVER_STATUS(false),
|
||||
|
||||
@@ -200,7 +200,7 @@ public class BillingEvent extends BillingBase {
|
||||
checkState(
|
||||
instance.reason.hasPeriodYears() == (instance.periodYears != null),
|
||||
"Period years must be set if and only if reason is "
|
||||
+ "CREATE, FEE_EARLY_ACCESS, RENEW, RESTORE or TRANSFER.");
|
||||
+ "CREATE, FEE_EARLY_ACCESS, FEE_EXPIRY_ACCESS, RENEW, RESTORE or TRANSFER.");
|
||||
}
|
||||
checkState(
|
||||
instance.getFlags().contains(Flag.SYNTHETIC) == (instance.syntheticCreationTime != null),
|
||||
|
||||
@@ -51,6 +51,10 @@ public abstract class BaseFee extends ImmutableObject {
|
||||
public enum FeeType {
|
||||
CREATE("create"),
|
||||
EAP("Early Access Period, fee expires: %s", "Early Access Period"),
|
||||
/**
|
||||
* A one-time fee for registering a recently dropped domain, during the Expiry Access Period.
|
||||
*/
|
||||
XAP("Expiry Access Period, fee expires: %s", "Expiry Access Period"),
|
||||
RENEW("renew"),
|
||||
RESTORE("restore"),
|
||||
/**
|
||||
|
||||
@@ -52,6 +52,7 @@ import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import google.registry.persistence.converter.AllocationTokenStatusTransitionUserType;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import jakarta.persistence.AttributeOverride;
|
||||
import jakarta.persistence.AttributeOverrides;
|
||||
import jakarta.persistence.Column;
|
||||
@@ -61,6 +62,7 @@ import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Index;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -374,28 +376,39 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
return ALLOCATION_TOKENS_CACHE.getAll(keys);
|
||||
}
|
||||
|
||||
/** A cache that loads the {@link AllocationToken} object for a given AllocationToken VKey. */
|
||||
private static final LoadingCache<VKey<AllocationToken>, Optional<AllocationToken>>
|
||||
ALLOCATION_TOKENS_CACHE =
|
||||
CacheUtils.newCacheBuilder(getSingletonCacheRefreshDuration())
|
||||
.build(
|
||||
new CacheLoader<>() {
|
||||
@Override
|
||||
public Optional<AllocationToken> load(VKey<AllocationToken> key) {
|
||||
return tm().reTransact(() -> tm().loadByKeyIfPresent(key));
|
||||
}
|
||||
@VisibleForTesting
|
||||
public static void setCacheForTest(Optional<Duration> expiry) {
|
||||
Duration effectiveExpiry = expiry.orElse(getSingletonCacheRefreshDuration());
|
||||
ALLOCATION_TOKENS_CACHE = createAllocationTokensCache(effectiveExpiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends VKey<AllocationToken>, ? extends Optional<AllocationToken>>
|
||||
loadAll(Set<? extends VKey<AllocationToken>> keys) {
|
||||
return tm().reTransact(
|
||||
() ->
|
||||
keys.stream()
|
||||
.collect(
|
||||
toImmutableMap(
|
||||
key -> key, key -> tm().loadByKeyIfPresent(key))));
|
||||
}
|
||||
});
|
||||
/** A cache that loads the {@link AllocationToken} object for a given AllocationToken VKey. */
|
||||
@NonFinalForTesting
|
||||
private static LoadingCache<VKey<AllocationToken>, Optional<AllocationToken>>
|
||||
ALLOCATION_TOKENS_CACHE = createAllocationTokensCache(getSingletonCacheRefreshDuration());
|
||||
|
||||
private static LoadingCache<VKey<AllocationToken>, Optional<AllocationToken>>
|
||||
createAllocationTokensCache(Duration expiry) {
|
||||
return CacheUtils.newCacheBuilder(expiry)
|
||||
.build(
|
||||
new CacheLoader<>() {
|
||||
@Override
|
||||
public Optional<AllocationToken> load(VKey<AllocationToken> key) {
|
||||
return tm().reTransact(() -> tm().loadByKeyIfPresent(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends VKey<AllocationToken>, ? extends Optional<AllocationToken>>
|
||||
loadAll(Set<? extends VKey<AllocationToken>> keys) {
|
||||
return tm().reTransact(
|
||||
() ->
|
||||
keys.stream()
|
||||
.collect(
|
||||
toImmutableMap(
|
||||
key -> key, key -> tm().loadByKeyIfPresent(key))));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public VKey<AllocationToken> createVKey() {
|
||||
|
||||
@@ -265,6 +265,18 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
@Column(nullable = false)
|
||||
boolean blockPremiumNames;
|
||||
|
||||
/**
|
||||
* Whether this registrar has opted into participating in the Expiry Access Period (XAP).
|
||||
*
|
||||
* <p>If this is false, any domain currently in XAP will appear as reserved/unavailable on checks
|
||||
* and creates.
|
||||
*
|
||||
* <p>TODO(mcilwain): Drop the temporary database default for expiry_access_period_enabled in a
|
||||
* subsequent schema migration once this code is deployed.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
boolean expiryAccessPeriodEnabled;
|
||||
|
||||
// Authentication.
|
||||
|
||||
/** X.509 PEM client certificate(s) used to authenticate registrar to EPP service. */
|
||||
@@ -560,6 +572,10 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
return blockPremiumNames;
|
||||
}
|
||||
|
||||
public boolean getExpiryAccessPeriodEnabled() {
|
||||
return expiryAccessPeriodEnabled;
|
||||
}
|
||||
|
||||
public boolean getContactsRequireSyncing() {
|
||||
return contactsRequireSyncing;
|
||||
}
|
||||
@@ -654,6 +670,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
.put("whoisServer", getWhoisServer())
|
||||
.putListOfStrings("rdapBaseUrls", getRdapBaseUrls())
|
||||
.put("blockPremiumNames", blockPremiumNames)
|
||||
.put("expiryAccessPeriodEnabled", expiryAccessPeriodEnabled)
|
||||
.put("url", url)
|
||||
.put("icannReferralEmail", getIcannReferralEmail())
|
||||
.put("driveFolderId", driveFolderId)
|
||||
@@ -918,6 +935,11 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setExpiryAccessPeriodEnabled(boolean expiryAccessPeriodEnabled) {
|
||||
getInstance().expiryAccessPeriodEnabled = expiryAccessPeriodEnabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setUrl(String url) {
|
||||
getInstance().url = url;
|
||||
return this;
|
||||
|
||||
@@ -56,6 +56,7 @@ import google.registry.model.EntityYamlUtils.OptionalDurationSerializer;
|
||||
import google.registry.model.EntityYamlUtils.OptionalStringSerializer;
|
||||
import google.registry.model.EntityYamlUtils.SortedEnumSetSerializer;
|
||||
import google.registry.model.EntityYamlUtils.SortedSetSerializer;
|
||||
import google.registry.model.EntityYamlUtils.TimedTransitionPropertyExpiryAccessPeriodModeDeserializer;
|
||||
import google.registry.model.EntityYamlUtils.TimedTransitionPropertyMoneyDeserializer;
|
||||
import google.registry.model.EntityYamlUtils.TimedTransitionPropertyTldStateDeserializer;
|
||||
import google.registry.model.EntityYamlUtils.TokenVKeyListDeserializer;
|
||||
@@ -76,6 +77,7 @@ import google.registry.persistence.EntityCallbacksListener.RecursivePostUpdate;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.converter.AllocationTokenVkeyListUserType;
|
||||
import google.registry.persistence.converter.BillingCostTransitionUserType;
|
||||
import google.registry.persistence.converter.ExpiryAccessPeriodModeTransitionUserType;
|
||||
import google.registry.persistence.converter.TldStateTransitionUserType;
|
||||
import google.registry.tldconfig.idn.IdnTableEnum;
|
||||
import google.registry.util.Idn;
|
||||
@@ -120,6 +122,8 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
|
||||
public static final boolean DEFAULT_ESCROW_ENABLED = false;
|
||||
public static final boolean DEFAULT_DNS_PAUSED = false;
|
||||
public static final ExpiryAccessPeriodMode DEFAULT_EXPIRY_ACCESS_PERIOD_MODE =
|
||||
ExpiryAccessPeriodMode.DISABLED;
|
||||
public static final Duration DEFAULT_ADD_GRACE_PERIOD = Duration.ofDays(5);
|
||||
public static final Duration DEFAULT_AUTO_RENEW_GRACE_PERIOD = Duration.ofDays(45);
|
||||
public static final Duration DEFAULT_REDEMPTION_GRACE_PERIOD = Duration.ofDays(30);
|
||||
@@ -197,6 +201,12 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
PDT
|
||||
}
|
||||
|
||||
/** The modes an Expiry Access Period (XAP) can be in at any given point in time. */
|
||||
public enum ExpiryAccessPeriodMode {
|
||||
DISABLED,
|
||||
ENABLED
|
||||
}
|
||||
|
||||
/** Returns the TLD for a given TLD, throwing if none exists. */
|
||||
public static Tld get(String tld) {
|
||||
return CACHE.get(tld).orElseThrow(() -> new TldNotFoundException(tld));
|
||||
@@ -426,6 +436,19 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
@Column(nullable = false)
|
||||
boolean dnsPaused = DEFAULT_DNS_PAUSED;
|
||||
|
||||
/**
|
||||
* Whether the Expiry Access Period following domain deletes is enabled for this TLD.
|
||||
*
|
||||
* <p>TODO(mcilwain): Once this Java code has been fully deployed to production, drop the
|
||||
* temporary database-level DEFAULT constraint on the "expiry_access_period_transitions" column in
|
||||
* the "Tld" table in a subsequent schema release.
|
||||
*/
|
||||
@Column(nullable = false, columnDefinition = "hstore")
|
||||
@Type(ExpiryAccessPeriodModeTransitionUserType.class)
|
||||
@JsonDeserialize(using = TimedTransitionPropertyExpiryAccessPeriodModeDeserializer.class)
|
||||
TimedTransitionProperty<ExpiryAccessPeriodMode> expiryAccessPeriodTransitions =
|
||||
TimedTransitionProperty.withInitialValue(DEFAULT_EXPIRY_ACCESS_PERIOD_MODE);
|
||||
|
||||
/**
|
||||
* The length of the add grace period for this TLD.
|
||||
*
|
||||
@@ -634,6 +657,14 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
return dnsPaused;
|
||||
}
|
||||
|
||||
public ExpiryAccessPeriodMode getExpiryAccessPeriodModeAt(Instant now) {
|
||||
return expiryAccessPeriodTransitions.getValueAtTime(now);
|
||||
}
|
||||
|
||||
public ImmutableSortedMap<Instant, ExpiryAccessPeriodMode> getExpiryAccessPeriodTransitions() {
|
||||
return expiryAccessPeriodTransitions.toValueMap();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDriveFolderId() {
|
||||
return driveFolderId;
|
||||
@@ -814,6 +845,56 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
/** Checks the validity of the TLD object, for use during building or deserializing. */
|
||||
public void validateState() {
|
||||
checkArgument(tldStr != null, "No registry TLD specified");
|
||||
// Check for canonical form by converting to an InternetDomainName and then back.
|
||||
checkArgument(
|
||||
InternetDomainName.isValid(tldStr)
|
||||
&& tldStr.equals(InternetDomainName.from(tldStr).toString()),
|
||||
"Cannot create registry for TLD that is not a valid, canonical domain name");
|
||||
// Check the validity of all TimedTransitionProperties to ensure that they have values for
|
||||
// START_INSTANT. The setters above have already checked this for new values, but also check
|
||||
// here to catch cases where we loaded an invalid TimedTransitionProperty from the database
|
||||
// and cloned it into a new builder, to block re-building a Tld in an invalid state.
|
||||
tldStateTransitions.checkValidity();
|
||||
createBillingCostTransitions.checkValidity();
|
||||
renewBillingCostTransitions.checkValidity();
|
||||
eapFeeSchedule.checkValidity();
|
||||
expiryAccessPeriodTransitions.checkValidity();
|
||||
// All costs must be in the expected currency.
|
||||
checkArgumentNotNull(getCurrency(), "Currency must be set");
|
||||
Predicate<Money> currencyCheck =
|
||||
(Money money) -> money.getCurrencyUnit().equals(currency) && money.isPositiveOrZero();
|
||||
checkArgument(
|
||||
currencyCheck.test(getRestoreBillingCost()),
|
||||
"Restore cost is negative or in the wrong currency");
|
||||
checkArgument(
|
||||
currencyCheck.test(getServerStatusChangeBillingCost()),
|
||||
"Server status change cost is negative or in the wrong currency");
|
||||
checkArgument(
|
||||
currencyCheck.test(getRegistryLockOrUnlockBillingCost()),
|
||||
"Registry lock/unlock cost is negative or in the wrong currency");
|
||||
checkArgument(
|
||||
getRenewBillingCostTransitions().values().stream().allMatch(currencyCheck),
|
||||
"Some renew cost(s) are negative or in the wrong currency");
|
||||
checkArgument(
|
||||
getCreateBillingCostTransitions().values().stream().allMatch(currencyCheck),
|
||||
"Some create cost(s) are negative or in the wrong currency");
|
||||
checkArgument(
|
||||
eapFeeSchedule.toValueMap().values().stream().allMatch(currencyCheck),
|
||||
"Some EAP fee cost(s) are negative or in the wrong currency'");
|
||||
checkArgumentNotNull(
|
||||
pricingEngineClassName, "All registries must have a configured pricing engine");
|
||||
checkArgument(
|
||||
dnsWriters != null && !dnsWriters.isEmpty(),
|
||||
"At least one DNS writer must be specified."
|
||||
+ " VoidDnsWriter can be used if DNS writing isn't desired");
|
||||
checkArgument(
|
||||
numDnsPublishLocks > 0,
|
||||
"Number of DNS publish locks must be positive. Use 1 for TLD-wide locks.");
|
||||
}
|
||||
|
||||
/** A builder for constructing {@link Tld} objects, since they are immutable. */
|
||||
public static class Builder extends Buildable.Builder<Tld> {
|
||||
public Builder() {}
|
||||
@@ -864,6 +945,13 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap<Instant, ExpiryAccessPeriodMode> transitionsMap) {
|
||||
getInstance().expiryAccessPeriodTransitions =
|
||||
TimedTransitionProperty.fromValueMap(transitionsMap);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDriveFolderId(String driveFolderId) {
|
||||
getInstance().driveFolderId = driveFolderId;
|
||||
return this;
|
||||
@@ -966,10 +1054,6 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
|
||||
public Builder setCreateBillingCostTransitions(
|
||||
ImmutableSortedMap<Instant, Money> createCostsMap) {
|
||||
checkArgumentNotNull(createCostsMap, "Create billing costs map cannot be null");
|
||||
checkArgument(
|
||||
createCostsMap.values().stream().allMatch(Money::isPositiveOrZero),
|
||||
"Create billing cost cannot be negative");
|
||||
getInstance().createBillingCostTransitions =
|
||||
TimedTransitionProperty.fromValueMap(createCostsMap);
|
||||
return this;
|
||||
@@ -1012,17 +1096,12 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
}
|
||||
|
||||
public Builder setRestoreBillingCost(Money amount) {
|
||||
checkArgument(amount.isPositiveOrZero(), "restoreBillingCost cannot be negative");
|
||||
getInstance().restoreBillingCost = amount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRenewBillingCostTransitions(
|
||||
ImmutableSortedMap<Instant, Money> renewCostsMap) {
|
||||
checkArgumentNotNull(renewCostsMap, "Renew billing costs map cannot be null");
|
||||
checkArgument(
|
||||
renewCostsMap.values().stream().allMatch(Money::isPositiveOrZero),
|
||||
"Renew billing cost cannot be negative");
|
||||
getInstance().renewBillingCostTransitions =
|
||||
TimedTransitionProperty.fromValueMap(renewCostsMap);
|
||||
return this;
|
||||
@@ -1030,10 +1109,6 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
|
||||
/** Sets the EAP fee schedule for the TLD. */
|
||||
public Builder setEapFeeSchedule(ImmutableSortedMap<Instant, Money> eapFeeSchedule) {
|
||||
checkArgumentNotNull(eapFeeSchedule, "EAP fee schedule cannot be null");
|
||||
checkArgument(
|
||||
eapFeeSchedule.values().stream().allMatch(Money::isPositiveOrZero),
|
||||
"EAP fee cannot be negative");
|
||||
getInstance().eapFeeSchedule = TimedTransitionProperty.fromValueMap(eapFeeSchedule);
|
||||
return this;
|
||||
}
|
||||
@@ -1051,14 +1126,11 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
}
|
||||
|
||||
public Builder setServerStatusChangeBillingCost(Money amount) {
|
||||
checkArgument(
|
||||
amount.isPositiveOrZero(), "Server status change billing cost cannot be negative");
|
||||
getInstance().serverStatusChangeBillingCost = amount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRegistryLockOrUnlockBillingCost(Money amount) {
|
||||
checkArgument(amount.isPositiveOrZero(), "Registry lock/unlock cost cannot be negative");
|
||||
getInstance().registryLockOrUnlockBillingCost = amount;
|
||||
return this;
|
||||
}
|
||||
@@ -1120,58 +1192,10 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
|
||||
@Override
|
||||
public Tld build() {
|
||||
final Tld instance = getInstance();
|
||||
// Pick up the name of the associated TLD from the instance object.
|
||||
String tldName = instance.tldStr;
|
||||
checkArgument(tldName != null, "No registry TLD specified");
|
||||
// Check for canonical form by converting to an InternetDomainName and then back.
|
||||
checkArgument(
|
||||
InternetDomainName.isValid(tldName)
|
||||
&& tldName.equals(InternetDomainName.from(tldName).toString()),
|
||||
"Cannot create registry for TLD that is not a valid, canonical domain name");
|
||||
// Check the validity of all TimedTransitionProperties to ensure that they have values for
|
||||
// START_INSTANT. The setters above have already checked this for new values, but also check
|
||||
// here to catch cases where we loaded an invalid TimedTransitionProperty from the database
|
||||
// and cloned it into a new builder, to block re-building a Tld in an invalid state.
|
||||
instance.tldStateTransitions.checkValidity();
|
||||
instance.createBillingCostTransitions.checkValidity();
|
||||
instance.renewBillingCostTransitions.checkValidity();
|
||||
instance.eapFeeSchedule.checkValidity();
|
||||
// All costs must be in the expected currency.
|
||||
checkArgumentNotNull(instance.getCurrency(), "Currency must be set");
|
||||
checkArgument(
|
||||
instance.getRestoreBillingCost().getCurrencyUnit().equals(instance.currency),
|
||||
"Restore cost must be in the TLD's currency");
|
||||
checkArgument(
|
||||
instance.getServerStatusChangeBillingCost().getCurrencyUnit().equals(instance.currency),
|
||||
"Server status change cost must be in the TLD's currency");
|
||||
checkArgument(
|
||||
instance.getRegistryLockOrUnlockBillingCost().getCurrencyUnit().equals(instance.currency),
|
||||
"Registry lock/unlock cost must be in the TLD's currency");
|
||||
Predicate<Money> currencyCheck =
|
||||
(Money money) -> money.getCurrencyUnit().equals(instance.currency);
|
||||
checkArgument(
|
||||
instance.getRenewBillingCostTransitions().values().stream().allMatch(currencyCheck),
|
||||
"Renew cost must be in the TLD's currency");
|
||||
checkArgument(
|
||||
instance.getCreateBillingCostTransitions().values().stream().allMatch(currencyCheck),
|
||||
"Create cost must be in the TLD's currency");
|
||||
checkArgument(
|
||||
instance.eapFeeSchedule.toValueMap().values().stream().allMatch(currencyCheck),
|
||||
"All EAP fees must be in the TLD's currency");
|
||||
checkArgumentNotNull(
|
||||
instance.pricingEngineClassName, "All registries must have a configured pricing engine");
|
||||
checkArgument(
|
||||
instance.dnsWriters != null && !instance.dnsWriters.isEmpty(),
|
||||
"At least one DNS writer must be specified."
|
||||
+ " VoidDnsWriter can be used if DNS writing isn't desired");
|
||||
// If not set explicitly, numDnsPublishLocks defaults to 1.
|
||||
instance.setDefaultNumDnsPublishLocks();
|
||||
checkArgument(
|
||||
instance.numDnsPublishLocks > 0,
|
||||
"Number of DNS publish locks must be positive. Use 1 for TLD-wide locks.");
|
||||
instance.tldStr = tldName;
|
||||
instance.tldUnicode = Idn.toUnicode(tldName);
|
||||
getInstance().setDefaultNumDnsPublishLocks();
|
||||
getInstance().validateState();
|
||||
getInstance().tldUnicode = Idn.toUnicode(getInstance().tldStr);
|
||||
return super.build();
|
||||
}
|
||||
}
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// Copyright 2026 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import google.registry.model.common.TimedTransitionProperty;
|
||||
import google.registry.model.tld.Tld.ExpiryAccessPeriodMode;
|
||||
|
||||
/** Hibernate custom type for {@link TimedTransitionProperty} of {@link ExpiryAccessPeriodMode}. */
|
||||
public class ExpiryAccessPeriodModeTransitionUserType
|
||||
extends TimedTransitionBaseUserType<ExpiryAccessPeriodMode> {
|
||||
|
||||
@Override
|
||||
String valueToString(ExpiryAccessPeriodMode value) {
|
||||
return value.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
ExpiryAccessPeriodMode stringToValue(String string) {
|
||||
return ExpiryAccessPeriodMode.valueOf(string);
|
||||
}
|
||||
}
|
||||
@@ -454,9 +454,8 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
// We must break the query up into chunks, because the in operator is limited to 30 subqueries.
|
||||
// Since it is possible for the same domain to show up more than once in our result list (if
|
||||
// we do a wildcard nameserver search that returns multiple nameservers used by the same
|
||||
// domain), we must create a set of resulting {@link Domain} objects. Use a sorted set,
|
||||
// and fetch all domains, to make sure that we can return the first domains in alphabetical
|
||||
// order.
|
||||
// domain), we must create a set of resulting {@link Domain}s. Use a sorted set, fetch all
|
||||
// domains, to make sure that we can return the first domains in alphabetical order.
|
||||
ImmutableSortedSet.Builder<Domain> domainSetBuilder =
|
||||
ImmutableSortedSet.orderedBy(Comparator.comparing(Domain::getDomainName));
|
||||
int numHostKeysSearched = 0;
|
||||
@@ -465,7 +464,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
replicaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
for (VKey<Host> hostKey : hostKeys) {
|
||||
for (VKey<Host> hostKey : chunk) {
|
||||
CriteriaQueryBuilder<Domain> queryBuilder =
|
||||
CriteriaQueryBuilder.create(replicaTm(), Domain.class)
|
||||
.whereFieldContains("nsHosts", hostKey)
|
||||
|
||||
@@ -185,7 +185,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
throw new UnprocessableEntityException(
|
||||
"A suffix after a wildcard in a nameserver lookup must be an in-bailiwick domain");
|
||||
}
|
||||
List<Host> hostList = new ArrayList<>();
|
||||
List<String> matchingFqhns = new ArrayList<>();
|
||||
for (String fqhn : ImmutableSortedSet.copyOf(domain.get().getSubordinateHosts())) {
|
||||
if (cursorString.isPresent() && (fqhn.compareTo(cursorString.get()) <= 0)) {
|
||||
continue;
|
||||
@@ -193,10 +193,22 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
// We can't just check that the host name starts with the initial query string, because
|
||||
// then the query ns.exam*.example.com would match against nameserver ns.example.com.
|
||||
if (partialStringQuery.matches(fqhn)) {
|
||||
Optional<Host> host =
|
||||
ForeignKeyUtils.loadResourceByCache(Host.class, fqhn, getRequestTime());
|
||||
if (shouldBeVisible(host)) {
|
||||
hostList.add(host.get());
|
||||
matchingFqhns.add(fqhn);
|
||||
}
|
||||
}
|
||||
List<Host> hostList = new ArrayList<>();
|
||||
int chunkSize = getStandardQuerySizeLimit();
|
||||
// Batch load from cache in chunks to avoid sequential N+1 database queries on cache misses.
|
||||
for (List<String> fqhnChunk : Iterables.partition(matchingFqhns, chunkSize)) {
|
||||
if (hostList.size() > rdapResultSetMaxSize) {
|
||||
break;
|
||||
}
|
||||
ImmutableMap<String, Host> cachedHosts =
|
||||
ForeignKeyUtils.loadResourcesByCache(Host.class, fqhnChunk, getRequestTime());
|
||||
for (String fqhn : fqhnChunk) {
|
||||
Host host = cachedHosts.get(fqhn);
|
||||
if (host != null && shouldBeVisible(host)) {
|
||||
hostList.add(host);
|
||||
if (hostList.size() > rdapResultSetMaxSize) {
|
||||
break;
|
||||
}
|
||||
@@ -204,10 +216,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
}
|
||||
}
|
||||
return makeSearchResults(
|
||||
hostList,
|
||||
IncompletenessWarningType.COMPLETE,
|
||||
domain.get().getSubordinateHosts().size(),
|
||||
CursorType.NAME);
|
||||
hostList, IncompletenessWarningType.COMPLETE, hostList.size(), CursorType.NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -119,6 +119,18 @@ public abstract class HttpException extends RuntimeException {
|
||||
}
|
||||
}
|
||||
|
||||
/** Exception that causes a 413 response. */
|
||||
public static final class PayloadTooLargeException extends HttpException {
|
||||
public PayloadTooLargeException(String message) {
|
||||
super(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, message, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResponseCodeString() {
|
||||
return "Payload Too Large";
|
||||
}
|
||||
}
|
||||
|
||||
/** Exception that causes a 404 response. */
|
||||
public static final class NotFoundException extends HttpException {
|
||||
public NotFoundException() {
|
||||
|
||||
@@ -32,7 +32,6 @@ import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.net.MediaType;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
@@ -42,6 +41,7 @@ import com.google.protobuf.ByteString;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.request.HttpException.PayloadTooLargeException;
|
||||
import google.registry.request.HttpException.UnsupportedMediaTypeException;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
@@ -50,6 +50,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -57,6 +58,8 @@ import java.util.Optional;
|
||||
@Module
|
||||
public final class RequestModule {
|
||||
|
||||
public static final int MAX_PAYLOAD_BYTES = 50 * 1024 * 1024;
|
||||
|
||||
private final HttpServletRequest req;
|
||||
private final HttpServletResponse rsp;
|
||||
private final AuthResult authResult;
|
||||
@@ -167,9 +170,37 @@ public final class RequestModule {
|
||||
|
||||
@Provides
|
||||
@Payload
|
||||
static String providePayloadAsString(HttpServletRequest req) {
|
||||
public static String providePayloadAsString(
|
||||
@Payload byte[] payloadBytes, HttpServletRequest req) {
|
||||
String charsetName = req.getCharacterEncoding();
|
||||
Charset charset;
|
||||
try {
|
||||
return CharStreams.toString(req.getReader());
|
||||
charset = (charsetName != null) ? Charset.forName(charsetName) : UTF_8;
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new UnsupportedMediaTypeException("Unsupported charset: " + charsetName, e);
|
||||
}
|
||||
return new String(payloadBytes, charset);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Payload
|
||||
public static byte[] providePayloadAsBytes(HttpServletRequest req) {
|
||||
try {
|
||||
if (req.getContentLengthLong() > MAX_PAYLOAD_BYTES) {
|
||||
throw new PayloadTooLargeException(
|
||||
String.format(
|
||||
"Payload size %d exceeds limit of %d bytes",
|
||||
req.getContentLengthLong(), MAX_PAYLOAD_BYTES));
|
||||
}
|
||||
if (req.getInputStream() == null) {
|
||||
return new byte[0];
|
||||
}
|
||||
byte[] bytes =
|
||||
ByteStreams.toByteArray(ByteStreams.limit(req.getInputStream(), MAX_PAYLOAD_BYTES + 1));
|
||||
if (bytes.length > MAX_PAYLOAD_BYTES) {
|
||||
throw new PayloadTooLargeException("Payload exceeds maximum allowed size");
|
||||
}
|
||||
return bytes;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -177,22 +208,8 @@ public final class RequestModule {
|
||||
|
||||
@Provides
|
||||
@Payload
|
||||
static byte[] providePayloadAsBytes(HttpServletRequest req) {
|
||||
try {
|
||||
return ByteStreams.toByteArray(req.getInputStream());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Payload
|
||||
static ByteString providePayloadAsByteString(HttpServletRequest req) {
|
||||
try {
|
||||
return ByteString.copyFrom(ByteStreams.toByteArray(req.getInputStream()));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
public static ByteString providePayloadAsByteString(@Payload byte[] payloadBytes) {
|
||||
return ByteString.copyFrom(payloadBytes);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -251,12 +268,10 @@ public final class RequestModule {
|
||||
|
||||
@Provides
|
||||
@OptionalJsonPayload
|
||||
public static Optional<JsonElement> provideJsonBody(HttpServletRequest req, Gson gson) {
|
||||
try {
|
||||
// GET requests return a null reader and thus a null JsonObject, which is fine
|
||||
return Optional.ofNullable(gson.fromJson(req.getReader(), JsonElement.class));
|
||||
} catch (IOException e) {
|
||||
public static Optional<JsonElement> provideJsonBody(@Payload String payloadString, Gson gson) {
|
||||
if (payloadString.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.ofNullable(gson.fromJson(payloadString, JsonElement.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static com.google.common.net.HttpHeaders.CONTENT_LENGTH;
|
||||
import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.net.MediaType;
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -37,6 +38,11 @@ import org.apache.commons.compress.utils.IOUtils;
|
||||
/** Utilities for common functionality relating to {@link URLConnection}s. */
|
||||
public final class UrlConnectionUtils {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
// 100 MB is arbitrary, not specifically selected for any reason
|
||||
public static final int MAX_RESPONSE_PAYLOAD_BYTES = 100 * 1024 * 1024;
|
||||
|
||||
private UrlConnectionUtils() {}
|
||||
|
||||
/**
|
||||
@@ -48,10 +54,26 @@ public final class UrlConnectionUtils {
|
||||
* @see HttpURLConnection#getErrorStream()
|
||||
*/
|
||||
public static byte[] getResponseBytes(HttpURLConnection connection) throws IOException {
|
||||
int responseCode = connection.getResponseCode();
|
||||
try (InputStream is =
|
||||
responseCode < 400 ? connection.getInputStream() : connection.getErrorStream()) {
|
||||
return ByteStreams.toByteArray(is);
|
||||
try {
|
||||
if (connection.getContentLengthLong() > MAX_RESPONSE_PAYLOAD_BYTES) {
|
||||
throw new IOException(
|
||||
String.format(
|
||||
"Response size %d exceeds limit of %d bytes",
|
||||
connection.getContentLengthLong(), MAX_RESPONSE_PAYLOAD_BYTES));
|
||||
} else if ((double) connection.getContentLengthLong() / MAX_RESPONSE_PAYLOAD_BYTES >= 0.9) {
|
||||
logger.atSevere().log(
|
||||
"Response from %s was within 90%% of the maximum response size", connection.getURL());
|
||||
}
|
||||
int responseCode = connection.getResponseCode();
|
||||
try (InputStream is =
|
||||
responseCode < 400 ? connection.getInputStream() : connection.getErrorStream()) {
|
||||
byte[] bytes =
|
||||
ByteStreams.toByteArray(ByteStreams.limit(is, MAX_RESPONSE_PAYLOAD_BYTES + 1));
|
||||
if (bytes.length > MAX_RESPONSE_PAYLOAD_BYTES) {
|
||||
throw new IOException("Response exceeds maximum allowed size");
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
return new byte[] {};
|
||||
}
|
||||
|
||||
@@ -164,6 +164,8 @@ public class ConfigureTldCommand extends MutatingCommand {
|
||||
if (Boolean.TRUE.equals(breakGlass)) {
|
||||
newTld = newTld.asBuilder().setBreakglassMode(true).build();
|
||||
}
|
||||
// Enforce any restrictions, e.g. "no negative fees"
|
||||
newTld.validateState();
|
||||
stageEntityChange(oldTld, newTld);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
@@ -39,6 +40,19 @@ abstract class CreateOrUpdatePremiumListCommand extends ConfirmingCommand {
|
||||
protected List<String> inputData;
|
||||
protected CurrencyUnit currency;
|
||||
|
||||
@Parameter(
|
||||
names = {"-d", "--dry_run"},
|
||||
description = "Does not execute the entity mutation")
|
||||
boolean dryRun;
|
||||
|
||||
@Parameter(
|
||||
names = {"--build_environment"},
|
||||
description =
|
||||
"DO NOT USE THIS FLAG ON THE COMMAND LINE! This flag indicates the command is being run"
|
||||
+ " by the build environment tools. This flag should never be used by a human user"
|
||||
+ " from the command line.")
|
||||
boolean buildEnv;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"-n", "--name"},
|
||||
@@ -68,4 +82,17 @@ abstract class CreateOrUpdatePremiumListCommand extends ConfirmingCommand {
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean dontRunCommand() {
|
||||
return dryRun;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkExecutionState() {
|
||||
checkArgument(
|
||||
!RegistryToolEnvironment.get().equals(RegistryToolEnvironment.PRODUCTION) || buildEnv,
|
||||
"The --build_environment flag must be used when running in production");
|
||||
return super.checkExecutionState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.tld.label.ReservedList;
|
||||
@@ -35,6 +37,19 @@ public abstract class CreateOrUpdateReservedListCommand extends ConfirmingComman
|
||||
|
||||
static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@Parameter(
|
||||
names = {"-d", "--dry_run"},
|
||||
description = "Does not execute the entity mutation")
|
||||
boolean dryRun;
|
||||
|
||||
@Parameter(
|
||||
names = {"--build_environment"},
|
||||
description =
|
||||
"DO NOT USE THIS FLAG ON THE COMMAND LINE! This flag indicates the command is being run"
|
||||
+ " by the build environment tools. This flag should never be used by a human user"
|
||||
+ " from the command line.")
|
||||
boolean buildEnv;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"-n", "--name"},
|
||||
@@ -74,4 +89,17 @@ public abstract class CreateOrUpdateReservedListCommand extends ConfirmingComman
|
||||
.collect(Collectors.joining(", "))
|
||||
+ "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean dontRunCommand() {
|
||||
return dryRun;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkExecutionState() {
|
||||
checkArgument(
|
||||
!RegistryToolEnvironment.get().equals(RegistryToolEnvironment.PRODUCTION) || buildEnv,
|
||||
"The --build_environment flag must be used when running in production");
|
||||
return super.checkExecutionState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@ import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
@@ -100,16 +103,16 @@ public final class DomainLockUtils {
|
||||
*
|
||||
* <p>This assumes that the lock object / domain in question has a pending lock or unlock.
|
||||
*/
|
||||
public RegistryLock verifyVerificationCode(String verificationCode, boolean isAdmin) {
|
||||
public RegistryLock verifyVerificationCode(String verificationCode, User user) {
|
||||
RegistryLock result =
|
||||
tm().transact(
|
||||
() -> {
|
||||
RegistryLock lock = getByVerificationCode(verificationCode);
|
||||
if (lock.getLockCompletionTime().isEmpty()) {
|
||||
return verifyAndApplyLock(lock, isAdmin);
|
||||
return verifyAndApplyLock(lock, user);
|
||||
} else if (lock.getUnlockRequestTime().isPresent()
|
||||
&& lock.getUnlockCompletionTime().isEmpty()) {
|
||||
return verifyAndApplyUnlock(lock, isAdmin);
|
||||
return verifyAndApplyUnlock(lock, user);
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
@@ -203,11 +206,18 @@ public final class DomainLockUtils {
|
||||
countdown));
|
||||
}
|
||||
|
||||
private RegistryLock verifyAndApplyLock(RegistryLock lock, boolean isAdmin) {
|
||||
private RegistryLock verifyAndApplyLock(RegistryLock lock, User user) {
|
||||
Instant now = tm().getTxTime();
|
||||
checkArgument(
|
||||
!lock.isLockRequestExpired(now), "The pending lock has expired; please try again");
|
||||
|
||||
UserRoles userRoles = user.getUserRoles();
|
||||
boolean isAdmin = userRoles.isAdmin();
|
||||
checkArgument(
|
||||
userRoles.hasPermission(lock.getRegistrarId(), ConsolePermission.REGISTRY_LOCK),
|
||||
"User %s does not have registry lock permission on registrar %s",
|
||||
user.getEmailAddress(),
|
||||
lock.getRegistrarId());
|
||||
checkArgument(!lock.isSuperuser() || isAdmin, "Non-admin user cannot complete admin lock");
|
||||
|
||||
RegistryLock newLock =
|
||||
@@ -217,13 +227,29 @@ public final class DomainLockUtils {
|
||||
return newLock;
|
||||
}
|
||||
|
||||
private RegistryLock verifyAndApplyUnlock(RegistryLock lock, boolean isAdmin) {
|
||||
private RegistryLock verifyAndApplyUnlock(RegistryLock lock, User user) {
|
||||
Instant now = tm().getTxTime();
|
||||
checkArgument(
|
||||
!lock.isUnlockRequestExpired(now), "The pending unlock has expired; please try again");
|
||||
|
||||
checkArgument(isAdmin || !lock.isSuperuser(), "Non-admin user cannot complete admin unlock");
|
||||
UserRoles userRoles = user.getUserRoles();
|
||||
boolean isAdmin = userRoles.isAdmin();
|
||||
checkArgument(
|
||||
userRoles.hasPermission(lock.getRegistrarId(), ConsolePermission.REGISTRY_LOCK),
|
||||
"User %s does not have registry lock permission on registrar %s",
|
||||
user.getEmailAddress(),
|
||||
lock.getRegistrarId());
|
||||
checkArgument(!lock.isSuperuser() || isAdmin, "Non-admin user cannot complete admin unlock");
|
||||
|
||||
// The pending unlock must still be the most recent RegistryLock for this domain. If a newer
|
||||
// lock exists (e.g. an admin/superuser lock applied after this unlock was requested or a
|
||||
// different unlock+relock), the verification code being redeemed is for a superseded row and
|
||||
// must not be allowed to strip the lock statuses that the newer row applied.
|
||||
Optional<RegistryLock> mostRecent = RegistryLockDao.getMostRecentByRepoId(lock.getRepoId());
|
||||
checkArgument(
|
||||
mostRecent.isPresent() && mostRecent.get().getRevisionId().equals(lock.getRevisionId()),
|
||||
"The pending unlock on %s has been superseded; please request a new unlock",
|
||||
lock.getDomainName());
|
||||
RegistryLock newLock =
|
||||
RegistryLockDao.save(lock.asBuilder().setUnlockCompletionTime(now).build());
|
||||
removeLockStatuses(newLock, isAdmin, now);
|
||||
|
||||
@@ -91,10 +91,11 @@ public class ShellCommand implements Command {
|
||||
* flags aren't available in the constructor, so we have to do it in the {@link #run} function.
|
||||
*/
|
||||
private final CommandRunner originalRunner;
|
||||
|
||||
private final LineReader lineReader;
|
||||
private final Clock clock;
|
||||
|
||||
private LineReader lineReader;
|
||||
private Terminal terminal;
|
||||
private JCommander jcommanderForCompletions;
|
||||
private String prompt = null;
|
||||
|
||||
@Parameter(
|
||||
@@ -118,21 +119,22 @@ public class ShellCommand implements Command {
|
||||
""")
|
||||
boolean encapsulateOutput = false;
|
||||
|
||||
ShellCommand(CommandRunner runner) throws IOException {
|
||||
this(TerminalBuilder.terminal(), new SystemClock(), runner);
|
||||
prompt = "nom > ";
|
||||
lineReader.variable(LineReader.HISTORY_FILE, Path.of(USER_HOME.value(), HISTORY_FILE));
|
||||
ShellCommand(CommandRunner runner) {
|
||||
this.originalRunner = runner;
|
||||
this.clock = new SystemClock();
|
||||
this.prompt = "nom > ";
|
||||
}
|
||||
|
||||
ShellCommand(Terminal terminal, Clock clock, CommandRunner runner) {
|
||||
this.originalRunner = runner;
|
||||
this.terminal = terminal;
|
||||
this.lineReader = LineReaderBuilder.builder().terminal(terminal).build();
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
private void setPrompt(RegistryToolEnvironment environment, boolean alert) {
|
||||
// Do not set the prompt in tests.
|
||||
if (lineReader.getTerminal() instanceof DumbTerminal) {
|
||||
if (lineReader == null || lineReader.getTerminal() instanceof DumbTerminal) {
|
||||
return;
|
||||
}
|
||||
prompt =
|
||||
@@ -143,7 +145,10 @@ public class ShellCommand implements Command {
|
||||
}
|
||||
|
||||
public ShellCommand buildCompletions(JCommander jcommander) {
|
||||
((LineReaderImpl) lineReader).setCompleter(new JCommanderCompleter(jcommander));
|
||||
this.jcommanderForCompletions = jcommander;
|
||||
if (lineReader != null) {
|
||||
((LineReaderImpl) lineReader).setCompleter(new JCommanderCompleter(jcommander));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -209,6 +214,19 @@ public class ShellCommand implements Command {
|
||||
/** Run the shell until the user presses "Ctrl-D". */
|
||||
@Override
|
||||
public void run() {
|
||||
if (lineReader == null) {
|
||||
try {
|
||||
this.terminal = TerminalBuilder.terminal();
|
||||
this.lineReader = LineReaderBuilder.builder().terminal(terminal).build();
|
||||
lineReader.variable(LineReader.HISTORY_FILE, Path.of(USER_HOME.value(), HISTORY_FILE));
|
||||
if (jcommanderForCompletions != null) {
|
||||
((LineReaderImpl) lineReader)
|
||||
.setCompleter(new JCommanderCompleter(jcommanderForCompletions));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to initialize terminal", e);
|
||||
}
|
||||
}
|
||||
// Wrap standard output and error if requested. We have to do so here in run because the flags
|
||||
// haven't been processed in the constructor.
|
||||
CommandRunner runner =
|
||||
|
||||
@@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.util.ListNamingUtils.convertFilePathToName;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.base.Strings;
|
||||
import google.registry.model.tld.label.PremiumList;
|
||||
@@ -30,27 +29,11 @@ import java.nio.file.Files;
|
||||
@Parameters(separators = " =", commandDescription = "Update a PremiumList in Database.")
|
||||
class UpdatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-d", "--dry_run"},
|
||||
description = "Does not execute the entity mutation")
|
||||
boolean dryRun;
|
||||
|
||||
@Parameter(
|
||||
names = {"--build_environment"},
|
||||
description =
|
||||
"DO NOT USE THIS FLAG ON THE COMMAND LINE! This flag indicates the command is being run"
|
||||
+ " by the build environment tools. This flag should never be used by a human user"
|
||||
+ " from the command line.")
|
||||
boolean buildEnv;
|
||||
|
||||
// Indicates if there is a new change made by this command
|
||||
private boolean newChange = false;
|
||||
|
||||
@Override
|
||||
protected String prompt() throws Exception {
|
||||
checkArgument(
|
||||
!RegistryToolEnvironment.get().equals(RegistryToolEnvironment.PRODUCTION) || buildEnv,
|
||||
"The --build_environment flag must be used when running update_premium_list in production");
|
||||
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(inputFile) : name;
|
||||
PremiumList existingList =
|
||||
PremiumListDao.getLatestRevision(name)
|
||||
|
||||
@@ -14,12 +14,10 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.util.DiffUtils.prettyPrintEntityDeepDiff;
|
||||
import static google.registry.util.ListNamingUtils.convertFilePathToName;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.base.Strings;
|
||||
import google.registry.model.tld.label.ReservedList;
|
||||
@@ -30,28 +28,11 @@ import java.util.List;
|
||||
@Parameters(separators = " =", commandDescription = "Update a ReservedList.")
|
||||
final class UpdateReservedListCommand extends CreateOrUpdateReservedListCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-d", "--dry_run"},
|
||||
description = "Does not execute the entity mutation")
|
||||
boolean dryRun;
|
||||
|
||||
@Parameter(
|
||||
names = {"--build_environment"},
|
||||
description =
|
||||
"DO NOT USE THIS FLAG ON THE COMMAND LINE! This flag indicates the command is being run"
|
||||
+ " by the build environment tools. This flag should never be used by a human user"
|
||||
+ " from the command line.")
|
||||
boolean buildEnv;
|
||||
|
||||
// indicates if there is a new change made by this command
|
||||
private boolean newChange = true;
|
||||
|
||||
@Override
|
||||
protected String prompt() throws Exception {
|
||||
checkArgument(
|
||||
!RegistryToolEnvironment.get().equals(RegistryToolEnvironment.PRODUCTION) || buildEnv,
|
||||
"The --build_environment flag must be used when running update_reserved_list in"
|
||||
+ " production");
|
||||
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(input) : name;
|
||||
ReservedList existingReservedList =
|
||||
ReservedList.get(name)
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright 2026 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/** Filter to inject security headers, including CSP, for defense-in-depth. */
|
||||
public class CspFilter implements Filter {
|
||||
|
||||
private static final String CSP_POLICY =
|
||||
"default-src 'self'; object-src 'none'; base-uri 'self';";
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
if (response instanceof HttpServletResponse httpResponse) {
|
||||
httpResponse.setHeader("Content-Security-Policy", CSP_POLICY);
|
||||
httpResponse.setHeader("X-Content-Type-Options", "nosniff");
|
||||
httpResponse.setHeader("X-Frame-Options", "DENY");
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
+17
-19
@@ -15,7 +15,7 @@
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.gson.annotations.Expose;
|
||||
@@ -34,7 +34,7 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
@Action(
|
||||
service = Service.CONSOLE,
|
||||
path = ConsoleRegistryLockVerifyAction.PATH,
|
||||
method = {GET},
|
||||
method = {POST},
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class ConsoleRegistryLockVerifyAction extends ConsoleApiAction {
|
||||
|
||||
@@ -54,9 +54,8 @@ public class ConsoleRegistryLockVerifyAction extends ConsoleApiAction {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getHandler(User user) {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.verifyVerificationCode(lockVerificationCode, user.getUserRoles().isAdmin());
|
||||
protected void postHandler(User user) {
|
||||
RegistryLock lock = domainLockUtils.verifyVerificationCode(lockVerificationCode, user);
|
||||
RegistryLockAction action =
|
||||
lock.getUnlockCompletionTime().isPresent()
|
||||
? RegistryLockAction.UNLOCKED
|
||||
@@ -65,20 +64,19 @@ public class ConsoleRegistryLockVerifyAction extends ConsoleApiAction {
|
||||
new RegistryLockVerificationResponse(
|
||||
Ascii.toLowerCase(action.toString()), lock.getDomainName(), lock.getRegistrarId());
|
||||
tm().transact(
|
||||
() -> {
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(
|
||||
action == RegistryLockAction.LOCKED
|
||||
? ConsoleUpdateHistory.Type.REGISTRY_LOCK
|
||||
: ConsoleUpdateHistory.Type.REGISTRY_UNLOCK)
|
||||
.setDescription(
|
||||
String.format(
|
||||
"%s%s%s",
|
||||
lock.getRegistrarId(),
|
||||
ConsoleUpdateHistory.DESCRIPTION_SEPARATOR,
|
||||
lockResponse)));
|
||||
});
|
||||
() ->
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(
|
||||
action == RegistryLockAction.LOCKED
|
||||
? ConsoleUpdateHistory.Type.REGISTRY_LOCK
|
||||
: ConsoleUpdateHistory.Type.REGISTRY_UNLOCK)
|
||||
.setDescription(
|
||||
String.format(
|
||||
"%s%s%s",
|
||||
lock.getRegistrarId(),
|
||||
ConsoleUpdateHistory.DESCRIPTION_SEPARATOR,
|
||||
lockResponse))));
|
||||
consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(lockResponse));
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.google.api.services.directory.model.UserName;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
@@ -58,6 +59,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -69,6 +71,7 @@ import javax.annotation.Nullable;
|
||||
public class ConsoleUsersAction extends ConsoleApiAction {
|
||||
static final String PATH = "/console-api/users";
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final int PASSWORD_LENGTH = 16;
|
||||
|
||||
private final String registrarId;
|
||||
@@ -184,11 +187,20 @@ public class ConsoleUsersAction extends ConsoleApiAction {
|
||||
|
||||
// User has no registrars assigned
|
||||
if (updatedUser.getUserRoles().getRegistrarRoles().isEmpty()) {
|
||||
try {
|
||||
directory.users().delete(email).execute();
|
||||
} catch (IOException e) {
|
||||
setFailedResponse("Failed to delete the user workspace account", SC_INTERNAL_SERVER_ERROR);
|
||||
throw e;
|
||||
// Only delete the Workspace account if runCreate() provably minted it. Addresses that
|
||||
// reached this row via CLI create_user, OteAccountBuilder, or other means must survive
|
||||
if (isConsoleMintedAddress(email)) {
|
||||
try {
|
||||
directory.users().delete(email).execute();
|
||||
} catch (IOException e) {
|
||||
setFailedResponse(
|
||||
"Failed to delete the user workspace account", SC_INTERNAL_SERVER_ERROR);
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
logger.atInfo().log(
|
||||
"Skipping Workspace deletion for %s because the console did not mint the account",
|
||||
email);
|
||||
}
|
||||
|
||||
VKey<User> key = VKey.create(User.class, email);
|
||||
@@ -368,6 +380,22 @@ public class ConsoleUsersAction extends ConsoleApiAction {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* True iff {@code email} matches the exact shape produced by {@link #runCreate()} for the current
|
||||
* {@code registrarId}: {@code <3 alnum>.<registrarId>@<gSuiteDomainName>}.
|
||||
*
|
||||
* <p>Addresses of any other shape were provisioned outside this action and should not be passed
|
||||
* to {@code directory.users().delete()}.
|
||||
*/
|
||||
private boolean isConsoleMintedAddress(String email) {
|
||||
return email.endsWith("@" + gSuiteDomainName)
|
||||
&& Pattern.matches(
|
||||
String.format(
|
||||
"^[a-zA-Z0-9]{3}\\.%s@%s$",
|
||||
Pattern.quote(registrarId), Pattern.quote(gSuiteDomainName)),
|
||||
email);
|
||||
}
|
||||
|
||||
public record UserData(
|
||||
@Expose String emailAddress,
|
||||
@Expose String registryLockEmailAddress,
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ public class RdapRegistrarFieldsAction extends ConsoleApiAction {
|
||||
newRegistrarBuilder.setPhoneNumber(providedRegistrar.getPhoneNumber());
|
||||
updates.add("PHONE");
|
||||
}
|
||||
if (!providedRegistrar.getFaxNumber().equals(savedRegistrar.getPhoneNumber())) {
|
||||
if (!providedRegistrar.getFaxNumber().equals(savedRegistrar.getFaxNumber())) {
|
||||
newRegistrarBuilder.setFaxNumber(providedRegistrar.getFaxNumber());
|
||||
updates.add("FAX");
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ import javax.xml.XMLConstants;
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Result;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerConfigurationException;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
@@ -62,7 +62,7 @@ public class XmlTransformer {
|
||||
private static final String SYSTEM_ID = "<default system id>";
|
||||
|
||||
/** A transformer factory for the {@link #prettyPrint} method. */
|
||||
private static final TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
||||
private static final TransformerFactory transformerFactory = createTransformerFactory();
|
||||
|
||||
/** A {@link JAXBContext} (thread-safe) to use for marshaling and unmarshaling. */
|
||||
private final JAXBContext jaxbContext;
|
||||
@@ -156,21 +156,24 @@ public class XmlTransformer {
|
||||
// Plain old parsing exceptions have a SAXParseException with no further cause.
|
||||
if (e.getLinkedException() instanceof SAXParseException sae
|
||||
&& e.getLinkedException().getCause() == null) {
|
||||
throw new XmlException(String.format(
|
||||
"Syntax error at line %d, column %d: %s",
|
||||
sae.getLineNumber(),
|
||||
sae.getColumnNumber(),
|
||||
nullToEmpty(sae.getMessage()).replaceAll(""", "")));
|
||||
throw new XmlException(
|
||||
String.format(
|
||||
"Syntax error at line %d, column %d: %s",
|
||||
sae.getLineNumber(),
|
||||
sae.getColumnNumber(),
|
||||
nullToEmpty(sae.getMessage()).replace(""", "")));
|
||||
}
|
||||
// These get thrown for attempted XXE attacks.
|
||||
if (e.getLinkedException() instanceof XMLStreamException xse) {
|
||||
throw new XmlException(String.format(
|
||||
"Syntax error at line %d, column %d: %s",
|
||||
xse.getLocation().getLineNumber(),
|
||||
xse.getLocation().getColumnNumber(),
|
||||
nullToEmpty(xse.getMessage())
|
||||
.replaceAll("^.*\nMessage: ", "") // Strip an ugly prefix from XMLStreamException.
|
||||
.replaceAll(""", "")));
|
||||
throw new XmlException(
|
||||
String.format(
|
||||
"Syntax error at line %d, column %d: %s",
|
||||
xse.getLocation().getLineNumber(),
|
||||
xse.getLocation().getColumnNumber(),
|
||||
nullToEmpty(xse.getMessage())
|
||||
.replaceAll(
|
||||
"^.*\nMessage: ", "") // Strip an ugly prefix from XMLStreamException.
|
||||
.replace(""", "")));
|
||||
}
|
||||
throw new XmlException(e);
|
||||
} catch (JAXBException | XMLStreamException | IOException e) {
|
||||
@@ -230,27 +233,6 @@ public class XmlTransformer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and streams {@code root} as characters, always using strict validation.
|
||||
*
|
||||
* <p>The root object must be annotated with {@link jakarta.xml.bind.annotation.XmlRootElement}.
|
||||
* This method will verify that your object strictly conforms to {@link #schema}. Because the
|
||||
* output is streamed, {@link XmlException} will most likely be thrown <i>after</i> output has
|
||||
* been written.
|
||||
*
|
||||
* @param root the object to write
|
||||
* @param result to write the output to
|
||||
* @throws XmlException to rethrow {@link JAXBException}.
|
||||
*/
|
||||
public void marshalStrict(Object root, Result result) throws XmlException {
|
||||
try {
|
||||
getMarshaller(schema, ImmutableMap.of())
|
||||
.marshal(checkNotNull(root, "root"), checkNotNull(result, "result"));
|
||||
} catch (JAXBException e) {
|
||||
throw new XmlException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns new instance of {@link XmlFragmentMarshaller}. */
|
||||
public XmlFragmentMarshaller createFragmentMarshaller() {
|
||||
return new XmlFragmentMarshaller(jaxbContext, schema);
|
||||
@@ -327,4 +309,14 @@ public class XmlTransformer {
|
||||
public static String prettyPrint(byte[] xmlBytes) {
|
||||
return prettyPrint(new String(xmlBytes, UTF_8));
|
||||
}
|
||||
|
||||
private static TransformerFactory createTransformerFactory() {
|
||||
TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
||||
try {
|
||||
transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
||||
} catch (TransformerConfigurationException e) {
|
||||
throw new RuntimeException(e); // this should never happen
|
||||
}
|
||||
return transformerFactory;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,15 +21,12 @@
|
||||
-- "tld":"","tlds":["a.how"],"icannActivityReportField":"srs-dom-check"}
|
||||
|
||||
SELECT
|
||||
-- Remove quotation marks from tld fields.
|
||||
REGEXP_EXTRACT(tld, '^"(.*)"$') AS tld,
|
||||
tld,
|
||||
activityReportField AS metricName,
|
||||
COUNT(*) AS count
|
||||
FROM (
|
||||
SELECT
|
||||
-- TODO(b/32486667): Replace with JSON.parse() UDF when available for views
|
||||
SPLIT(
|
||||
REGEXP_EXTRACT(JSON_EXTRACT(json, '$.tlds'), r'^\[(.*)\]$')) AS tlds,
|
||||
JSON_EXTRACT_STRING_ARRAY(json, '$.tlds') AS tlds,
|
||||
JSON_EXTRACT_SCALAR(json,
|
||||
'$.resourceType') AS resourceType,
|
||||
JSON_EXTRACT_SCALAR(json,
|
||||
@@ -43,10 +40,10 @@ FROM (
|
||||
WHERE
|
||||
STARTS_WITH(jsonPayload.message, "FLOW-LOG-SIGNATURE-METADATA")
|
||||
AND _TABLE_SUFFIX BETWEEN '%FIRST_DAY_OF_MONTH%' AND '%LAST_DAY_OF_MONTH%')
|
||||
) AS regexes
|
||||
) AS json_parsed
|
||||
JOIN
|
||||
-- Unnest the JSON-parsed tlds.
|
||||
UNNEST(regexes.tlds) AS tld
|
||||
UNNEST(json_parsed.tlds) AS tld
|
||||
-- Exclude cases that can't be tabulated correctly, where activityReportField
|
||||
-- is null/empty, or TLD is null/empty despite being a domain flow.
|
||||
WHERE
|
||||
|
||||
@@ -24,11 +24,14 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -100,4 +103,39 @@ public class MultilayerDomainCacheTest {
|
||||
verify(cacheMetrics).recordLookup("Domain", CacheMetrics.CacheHitType.MISS_NONEXISTENT);
|
||||
verifyNoMoreInteractions(cacheMetrics);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoad_filtersOutDeletedDomain() {
|
||||
Domain domain =
|
||||
persistActiveDomain("example.tld")
|
||||
.asBuilder()
|
||||
.setDeletionTime(clock.now().plus(Duration.ofDays(1)))
|
||||
.build();
|
||||
when(jedisClient.get(Domain.class, "example.tld")).thenReturn(Optional.of(domain));
|
||||
assertThat(cache.loadByDomainName("example.tld")).hasValue(domain);
|
||||
|
||||
clock.advanceBy(Duration.ofDays(2));
|
||||
assertThat(cache.loadByDomainName("example.tld")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoad_projectsToCurrentTime() {
|
||||
Domain domain =
|
||||
persistActiveDomain("example.tld")
|
||||
.asBuilder()
|
||||
.addGracePeriod(
|
||||
GracePeriod.create(
|
||||
GracePeriodStatus.ADD,
|
||||
"example.tld",
|
||||
clock.now().plus(Duration.ofDays(5)),
|
||||
"TheRegistrar",
|
||||
null))
|
||||
.build();
|
||||
when(jedisClient.get(Domain.class, "example.tld")).thenReturn(Optional.of(domain));
|
||||
assertThat(cache.loadByDomainName("example.tld").get().getGracePeriods())
|
||||
.containsExactlyElementsIn(domain.getGracePeriods());
|
||||
|
||||
clock.advanceBy(Duration.ofDays(10));
|
||||
assertThat(cache.loadByDomainName("example.tld").get().getGracePeriods()).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import google.registry.model.host.Host;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -38,12 +40,13 @@ public class MultilayerHostCacheTest {
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
private final SimplifiedJedisClient jedisClient = mock(SimplifiedJedisClient.class);
|
||||
private final FakeClock clock = new FakeClock();
|
||||
private final CacheMetrics cacheMetrics = mock(CacheMetrics.class);
|
||||
private MultilayerHostCache cache;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
cache = new MultilayerHostCache(jedisClient, cacheMetrics);
|
||||
cache = new MultilayerHostCache(jedisClient, clock, cacheMetrics);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,4 +83,18 @@ public class MultilayerHostCacheTest {
|
||||
verify(cacheMetrics).recordLookup("Host", CacheMetrics.CacheHitType.MISS_NONEXISTENT);
|
||||
verifyNoMoreInteractions(cacheMetrics);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoad_filtersOutDeletedHost() {
|
||||
Host host =
|
||||
persistActiveHost("ns1.example.tld")
|
||||
.asBuilder()
|
||||
.setDeletionTime(clock.now().plus(Duration.ofDays(1)))
|
||||
.build();
|
||||
when(jedisClient.get(Host.class, host.getRepoId())).thenReturn(Optional.of(host));
|
||||
assertThat(cache.loadByRepoId(host.getRepoId())).hasValue(host);
|
||||
|
||||
clock.advanceBy(Duration.ofDays(2));
|
||||
assertThat(cache.loadByRepoId(host.getRepoId())).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,16 +184,22 @@ class FlowReporterTest {
|
||||
|
||||
@Test
|
||||
void testRecordToLogs_metadata_invalidDomainName_stillGuessesTld() throws Exception {
|
||||
var maxLenTld = "a".repeat(63);
|
||||
var domainMaxLenTld = "a." + maxLenTld;
|
||||
var domainTldTooLong = "a." + maxLenTld + "b";
|
||||
when(flowReporter.eppInput.isDomainType()).thenReturn(true);
|
||||
when(flowReporter.eppInput.getSingleTargetId()).thenReturn(Optional.of("<foo@bar.com>"));
|
||||
when(flowReporter.eppInput.getTargetIds()).thenReturn(ImmutableList.of("<foo@bar.com>"));
|
||||
when(flowReporter.eppInput.getTargetIds())
|
||||
.thenReturn(ImmutableList.of("<foo@bar.com>", domainMaxLenTld, domainTldTooLong));
|
||||
flowReporter.recordToLogs();
|
||||
Map<String, Object> json =
|
||||
parseJsonMap(findFirstLogMessageByPrefix(handler, "FLOW-LOG-SIGNATURE-METADATA: "));
|
||||
assertThat(json).containsEntry("targetId", "<foo@bar.com>");
|
||||
assertThat(json).containsEntry("targetIds", ImmutableList.of("<foo@bar.com>"));
|
||||
assertThat(json).containsEntry("tld", "com>");
|
||||
assertThat(json).containsEntry("tlds", ImmutableList.of("com>"));
|
||||
assertThat(json)
|
||||
.containsEntry(
|
||||
"targetIds", ImmutableList.of("<foo@bar.com>", domainMaxLenTld, domainTldTooLong));
|
||||
assertThat(json).containsEntry("tld", "com-");
|
||||
assertThat(json).containsEntry("tlds", ImmutableList.of("com-", maxLenTld, maxLenTld + "..."));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -49,6 +49,8 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Ordering;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.config.RegistryConfigSettings;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.FlowUtils.GenericXmlSyntaxErrorException;
|
||||
import google.registry.flows.FlowUtils.NotLoggedInException;
|
||||
@@ -95,6 +97,7 @@ import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.model.tld.label.ReservedList;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.Money;
|
||||
@@ -2447,6 +2450,283 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
runFlowAssertResponse(outputXml);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFeeExtension_xapLabel_std_v1() throws Exception {
|
||||
createTld("tld");
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(true)
|
||||
.build());
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
|
||||
RegistryConfigSettings settings = RegistryConfig.CONFIG_SETTINGS.get();
|
||||
BigDecimal originalInitialFee =
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.initialFee.get("USD");
|
||||
BigDecimal originalFinalFee =
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.finalFee.get("USD");
|
||||
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.initialFee =
|
||||
ImmutableMap.of("USD", new BigDecimal("110.00"));
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.finalFee =
|
||||
ImmutableMap.of("USD", new BigDecimal("110.00"));
|
||||
|
||||
try {
|
||||
clock.setTo(Instant.parse("2009-01-06T00:00:00Z"));
|
||||
Instant deletionTime = Instant.parse("2009-01-01T00:00:00Z");
|
||||
persistDeletedDomain("example1.tld", deletionTime);
|
||||
|
||||
setEppInput("domain_check_fee_stdv1.xml");
|
||||
runFlowAssertResponse(loadFile("domain_check_fee_xap_response.xml"));
|
||||
} finally {
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.initialFee =
|
||||
ImmutableMap.of("USD", originalInitialFee);
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.finalFee =
|
||||
ImmutableMap.of("USD", originalFinalFee);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheck_xapLabel_notOptedIn_returnsReserved() throws Exception {
|
||||
createTld("tld");
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(false)
|
||||
.build());
|
||||
Instant deletionTime = clock.now().minus(Duration.ofDays(5));
|
||||
persistDeletedDomain("example1.tld", deletionTime);
|
||||
setEppInput("domain_check_one_tld.xml");
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "Reserved"),
|
||||
create(true, "example2.tld", null),
|
||||
create(true, "example3.tld", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheck_recentlyDeletedDomain_afterXapEnds_notOptedIn_returnsAvailable() throws Exception {
|
||||
// Once the 10-day XAP window expires after deletion (e.g. day 11), checking the domain should
|
||||
// return available (avail="1") without XAP fees or reservation blocks for non-opted-in
|
||||
// registrars.
|
||||
createTld("tld");
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(false)
|
||||
.build());
|
||||
Instant deletionTime = clock.now().minus(Duration.ofDays(11));
|
||||
persistDeletedDomain("example1.tld", deletionTime);
|
||||
setEppInput("domain_check_one_tld.xml");
|
||||
doCheckTest(
|
||||
create(true, "example1.tld", null),
|
||||
create(true, "example2.tld", null),
|
||||
create(true, "example3.tld", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheck_recentlyDeletedDomain_afterXapEnds_optedIn_returnsAvailable() throws Exception {
|
||||
// Once the 10-day XAP window expires after deletion (e.g. day 11), checking the domain should
|
||||
// return available (avail="1") without XAP fees for opted-in registrars.
|
||||
createTld("tld");
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(true)
|
||||
.build());
|
||||
Instant deletionTime = clock.now().minus(Duration.ofDays(11));
|
||||
persistDeletedDomain("example1.tld", deletionTime);
|
||||
setEppInput("domain_check_one_tld.xml");
|
||||
doCheckTest(
|
||||
create(true, "example1.tld", null),
|
||||
create(true, "example2.tld", null),
|
||||
create(true, "example3.tld", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheck_recentlyDeletedDomain_beforeXapStarts_notOptedIn_returnsAvailable()
|
||||
throws Exception {
|
||||
// If a domain was recently deleted but XAP mode is currently disabled on the TLD, checking the
|
||||
// domain should return available (avail="1") without XAP fees or reservation blocks for
|
||||
// non-opted-in registrars.
|
||||
createTld("tld");
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.DISABLED))
|
||||
.build());
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(false)
|
||||
.build());
|
||||
Instant deletionTime = clock.now().minus(Duration.ofDays(5));
|
||||
persistDeletedDomain("example1.tld", deletionTime);
|
||||
setEppInput("domain_check_one_tld.xml");
|
||||
doCheckTest(
|
||||
create(true, "example1.tld", null),
|
||||
create(true, "example2.tld", null),
|
||||
create(true, "example3.tld", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheck_recentlyDeletedDomain_beforeXapStarts_optedIn_returnsAvailable() throws Exception {
|
||||
// If a domain was recently deleted but XAP mode is currently disabled on the TLD, checking the
|
||||
// domain should return available (avail="1") without XAP fees for opted-in registrars.
|
||||
createTld("tld");
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.DISABLED))
|
||||
.build());
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(true)
|
||||
.build());
|
||||
Instant deletionTime = clock.now().minus(Duration.ofDays(5));
|
||||
persistDeletedDomain("example1.tld", deletionTime);
|
||||
setEppInput("domain_check_one_tld.xml");
|
||||
doCheckTest(
|
||||
create(true, "example1.tld", null),
|
||||
create(true, "example2.tld", null),
|
||||
create(true, "example3.tld", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheck_agpDeletedRegularDomain_duringXap_returnsAvailable() throws Exception {
|
||||
// A regular domain (registered without an XAP fee) deleted during its Add Grace Period (AGP) is
|
||||
// exempt from XAP, returning available (avail="1") without reservation blocks or XAP fees when
|
||||
// checked during active XAP on the TLD.
|
||||
createTld("tld");
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(
|
||||
START_INSTANT,
|
||||
Tld.ExpiryAccessPeriodMode.DISABLED,
|
||||
clock.now().minus(Duration.ofHours(1)),
|
||||
Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(false)
|
||||
.build());
|
||||
Instant creationTime = clock.now().minus(Duration.ofDays(2));
|
||||
Instant deletionTime = clock.now().minus(Duration.ofHours(1));
|
||||
DatabaseHelper.persistDomainAsDeleted(
|
||||
DatabaseHelper.newDomain("example1.tld")
|
||||
.asBuilder()
|
||||
.setCreationTimeForTest(creationTime)
|
||||
.build(),
|
||||
deletionTime);
|
||||
setEppInput("domain_check_one_tld.xml");
|
||||
doCheckTest(
|
||||
create(true, "example1.tld", null),
|
||||
create(true, "example2.tld", null),
|
||||
create(true, "example3.tld", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheck_agpDeletedXapDomain_duringXap_returnsAvailable() throws Exception {
|
||||
// A domain originally registered with an XAP fee deleted during its Add Grace Period (AGP) is
|
||||
// exempt from XAP, returning available (avail="1") without reservation blocks or XAP fees when
|
||||
// checked during active XAP on the TLD.
|
||||
createTld("tld");
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(false)
|
||||
.build());
|
||||
Instant creationTime = clock.now().minus(Duration.ofHours(2));
|
||||
Instant deletionTime = clock.now().minus(Duration.ofHours(1));
|
||||
DatabaseHelper.persistDomainAsDeleted(
|
||||
DatabaseHelper.newDomain("example1.tld")
|
||||
.asBuilder()
|
||||
.setCreationTimeForTest(creationTime)
|
||||
.build(),
|
||||
deletionTime);
|
||||
setEppInput("domain_check_one_tld.xml");
|
||||
doCheckTest(
|
||||
create(true, "example1.tld", null),
|
||||
create(true, "example2.tld", null),
|
||||
create(true, "example3.tld", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheck_anchorTenantDeletedAfterStandardAgp_duringXap_returnsReserved() throws Exception {
|
||||
// An anchor tenant domain deleted after standard AGP (5 days), e.g. on day 10 of its 30-day
|
||||
// anchor tenant AGP, is subject to XAP fees and reservation blocks (not exempt as an AGP
|
||||
// delete).
|
||||
createTld("tld");
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setAddGracePeriodLength(Duration.ofDays(5))
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(false)
|
||||
.build());
|
||||
Instant creationTime = clock.now().minus(Duration.ofDays(10));
|
||||
Instant deletionTime = clock.now().minus(Duration.ofHours(1));
|
||||
DatabaseHelper.persistDomainAsDeleted(
|
||||
DatabaseHelper.newDomain("example1.tld")
|
||||
.asBuilder()
|
||||
.setCreationTimeForTest(creationTime)
|
||||
.build(),
|
||||
deletionTime);
|
||||
setEppInput("domain_check_one_tld.xml");
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "Reserved"),
|
||||
create(true, "example2.tld", null),
|
||||
create(true, "example3.tld", null));
|
||||
}
|
||||
|
||||
static AllocationToken setUpDefaultToken() {
|
||||
return setUpDefaultToken("TheRegistrar");
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Ordering;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.config.RegistryConfigSettings;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.UnimplementedExtensionException;
|
||||
import google.registry.flows.EppRequestSource;
|
||||
@@ -113,6 +114,7 @@ import google.registry.flows.domain.DomainFlowUtils.FeeDescriptionMultipleMatche
|
||||
import google.registry.flows.domain.DomainFlowUtils.FeeDescriptionParseException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.FeesMismatchException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.FeesRequiredDuringEarlyAccessProgramException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.FeesRequiredDuringExpiryAccessPeriodException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.FeesRequiredForPremiumNameException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.InvalidDsRecordException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.InvalidIdnDomainLabelException;
|
||||
@@ -2688,6 +2690,427 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
+ "during the Early Access Program. The EAP fee is: USD 100.00");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_domainInXap_withoutFeeAck_throwsException() throws Exception {
|
||||
// Creating a domain during an active XAP tier without acknowledging the XAP fee in the EPP fee
|
||||
// extension should fail with FeesRequiredDuringExpiryAccessPeriodException.
|
||||
RegistryConfigSettings settings = RegistryConfig.CONFIG_SETTINGS.get();
|
||||
BigDecimal originalInitialFee =
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.initialFee.get("USD");
|
||||
BigDecimal originalFinalFee =
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.finalFee.get("USD");
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.initialFee =
|
||||
ImmutableMap.of("USD", new BigDecimal("100.00"));
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.finalFee =
|
||||
ImmutableMap.of("USD", new BigDecimal("100.00"));
|
||||
try {
|
||||
persistHosts();
|
||||
Instant deletionTime = clock.now().minus(Duration.ofHours(1));
|
||||
setXapForTld("tld", deletionTime);
|
||||
Exception e =
|
||||
assertThrows(FeesRequiredDuringExpiryAccessPeriodException.class, this::runFlow);
|
||||
assertThat(e)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Fees must be explicitly acknowledged when creating domains "
|
||||
+ "during the Expiry Access Period. The XAP fee is: USD 100.00");
|
||||
} finally {
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.initialFee =
|
||||
ImmutableMap.of("USD", originalInitialFee);
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.finalFee =
|
||||
ImmutableMap.of("USD", originalFinalFee);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_domainInXap_registrarNotOptedIn_throwsDomainReservedException()
|
||||
throws Exception {
|
||||
// When XAP is enabled on the TLD and the domain is in its XAP window, a registrar that has not
|
||||
// opted into XAP cannot register the domain and should receive a DomainReservedException.
|
||||
persistHosts();
|
||||
Instant deletionTime = clock.now().minus(Duration.ofHours(1));
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(false)
|
||||
.build());
|
||||
persistResource(
|
||||
new Domain.Builder()
|
||||
.setDomainName(getUniqueIdFromCommand())
|
||||
.setDeletionTime(deletionTime)
|
||||
.setRegistrationExpirationTime(clock.now().plus(Duration.ofDays(365)))
|
||||
.setCreationTime(clock.now().minus(Duration.ofDays(365)))
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setRepoId("9999-EXAMPLE")
|
||||
.build());
|
||||
Exception e = assertThrows(DomainReservedException.class, this::runFlow);
|
||||
assertThat(e).hasMessageThat().isEqualTo("example.tld is a reserved domain");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_domainInXap_optedIn_withFeeAck_chargesXapFee() throws Exception {
|
||||
// When an opted-in registrar acknowledges the XAP fee during an active XAP tier, domain
|
||||
// creation should succeed and persist a one-time XAP fee (Reason.FEE_EXPIRY_ACCESS) in
|
||||
// addition to the standard domain create fee (Reason.CREATE) and autorenew recurrence
|
||||
// (Reason.RENEW).
|
||||
RegistryConfigSettings settings = RegistryConfig.CONFIG_SETTINGS.get();
|
||||
BigDecimal originalInitialFee =
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.initialFee.get("USD");
|
||||
BigDecimal originalFinalFee =
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.finalFee.get("USD");
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.initialFee =
|
||||
ImmutableMap.of("USD", new BigDecimal("100.00"));
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.finalFee =
|
||||
ImmutableMap.of("USD", new BigDecimal("100.00"));
|
||||
try {
|
||||
persistHosts();
|
||||
Instant deletionTime = Instant.parse("1999-04-03T21:00:00.0Z");
|
||||
setXapForTld("tld", deletionTime);
|
||||
setEppInput(
|
||||
"domain_create_eap_fee.xml",
|
||||
new ImmutableMap.Builder<String, String>()
|
||||
.putAll(FEE_STD_1_0_MAP)
|
||||
.put("DESCRIPTION_1", "create")
|
||||
.put("DESCRIPTION_2", "Expiry Access Period")
|
||||
.build());
|
||||
runFlowAssertResponse(loadFile("domain_create_response_xap_fee.xml"));
|
||||
|
||||
Domain domain = reloadResourceByForeignKey();
|
||||
DomainHistory historyEntry = getHistoryEntries(domain, DomainHistory.class).get(0);
|
||||
assertBillingEvents(
|
||||
new BillingEvent.Builder()
|
||||
.setReason(Reason.CREATE)
|
||||
.setTargetId("example.tld")
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setPeriodYears(2)
|
||||
.setCost(Money.of(USD, 24))
|
||||
.setEventTime(clock.now())
|
||||
.setBillingTime(clock.now().plus(Tld.get("tld").getAddGracePeriodLength()))
|
||||
.setDomainHistory(historyEntry)
|
||||
.build(),
|
||||
new BillingEvent.Builder()
|
||||
.setReason(Reason.FEE_EXPIRY_ACCESS)
|
||||
.setTargetId("example.tld")
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setPeriodYears(1)
|
||||
.setCost(Money.of(USD, 100))
|
||||
.setEventTime(clock.now())
|
||||
.setBillingTime(clock.now().plus(Tld.get("tld").getAddGracePeriodLength()))
|
||||
.setDomainHistory(historyEntry)
|
||||
.build(),
|
||||
new BillingRecurrence.Builder()
|
||||
.setReason(Reason.RENEW)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setTargetId("example.tld")
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setEventTime(domain.getRegistrationExpirationTime())
|
||||
.setRecurrenceEndTime(END_INSTANT)
|
||||
.setDomainHistory(historyEntry)
|
||||
.build());
|
||||
} finally {
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.initialFee =
|
||||
ImmutableMap.of("USD", originalInitialFee);
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.finalFee =
|
||||
ImmutableMap.of("USD", originalFinalFee);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_premiumDomainInXap_optedIn_withFeeAck_chargesPremiumAndXapFees()
|
||||
throws Exception {
|
||||
// Creating a premium domain during active XAP should stack both fees, charging the premium
|
||||
// domain create fee (Reason.CREATE) plus the one-time XAP tier fee (Reason.FEE_EXPIRY_ACCESS).
|
||||
createTld("example");
|
||||
RegistryConfigSettings settings = RegistryConfig.CONFIG_SETTINGS.get();
|
||||
BigDecimal originalInitialFee =
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.initialFee.get("USD");
|
||||
BigDecimal originalFinalFee =
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.finalFee.get("USD");
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.initialFee =
|
||||
ImmutableMap.of("USD", new BigDecimal("100.00"));
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.finalFee =
|
||||
ImmutableMap.of("USD", new BigDecimal("100.00"));
|
||||
try {
|
||||
persistHosts();
|
||||
setEppInput("domain_create_premium_xap.xml");
|
||||
Instant deletionTime = Instant.parse("1999-04-03T21:00:00.0Z");
|
||||
setXapForTld("example", deletionTime);
|
||||
runFlowAssertResponse(loadFile("domain_create_response_premium_xap.xml"));
|
||||
|
||||
Domain domain = reloadResourceByForeignKey();
|
||||
DomainHistory historyEntry = getHistoryEntries(domain, DomainHistory.class).get(0);
|
||||
assertBillingEvents(
|
||||
new BillingEvent.Builder()
|
||||
.setReason(Reason.CREATE)
|
||||
.setTargetId("rich.example")
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setPeriodYears(2)
|
||||
.setCost(Money.of(USD, 200))
|
||||
.setEventTime(clock.now())
|
||||
.setBillingTime(clock.now().plus(Tld.get("example").getAddGracePeriodLength()))
|
||||
.setDomainHistory(historyEntry)
|
||||
.build(),
|
||||
new BillingEvent.Builder()
|
||||
.setReason(Reason.FEE_EXPIRY_ACCESS)
|
||||
.setTargetId("rich.example")
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setPeriodYears(1)
|
||||
.setCost(Money.of(USD, 100))
|
||||
.setEventTime(clock.now())
|
||||
.setBillingTime(clock.now().plus(Tld.get("example").getAddGracePeriodLength()))
|
||||
.setDomainHistory(historyEntry)
|
||||
.build(),
|
||||
new BillingRecurrence.Builder()
|
||||
.setReason(Reason.RENEW)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setTargetId("rich.example")
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setEventTime(domain.getRegistrationExpirationTime())
|
||||
.setRecurrenceEndTime(END_INSTANT)
|
||||
.setDomainHistory(historyEntry)
|
||||
.build());
|
||||
} finally {
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.initialFee =
|
||||
ImmutableMap.of("USD", originalInitialFee);
|
||||
settings.registryPolicy.domainExpiryAccessPeriod.finalFee =
|
||||
ImmutableMap.of("USD", originalFinalFee);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_recentlyDeletedDomain_afterXapEnds_notOptedIn_succeedsWithoutXapFee()
|
||||
throws Exception {
|
||||
// Once the 10-day XAP window expires after deletion (e.g. day 11), the domain returns to
|
||||
// standard pricing and can be registered without XAP fees or reservation blocks by
|
||||
// non-opted-in registrars.
|
||||
persistHosts();
|
||||
Instant deletionTime = clock.now().minus(Duration.ofDays(11));
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(false)
|
||||
.build());
|
||||
persistResource(
|
||||
new Domain.Builder()
|
||||
.setDomainName(getUniqueIdFromCommand())
|
||||
.setDeletionTime(deletionTime)
|
||||
.setRegistrationExpirationTime(clock.now().plus(Duration.ofDays(365)))
|
||||
.setCreationTime(clock.now().minus(Duration.ofDays(365)))
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setRepoId("9999-EXAMPLE")
|
||||
.build());
|
||||
doSuccessfulTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_recentlyDeletedDomain_afterXapEnds_optedIn_succeedsWithoutXapFee()
|
||||
throws Exception {
|
||||
// Once the 10-day XAP window expires after deletion (e.g. day 11), the domain returns to
|
||||
// standard pricing and can be registered without XAP fees by opted-in registrars.
|
||||
persistHosts();
|
||||
Instant deletionTime = clock.now().minus(Duration.ofDays(11));
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(true)
|
||||
.build());
|
||||
persistResource(
|
||||
new Domain.Builder()
|
||||
.setDomainName(getUniqueIdFromCommand())
|
||||
.setDeletionTime(deletionTime)
|
||||
.setRegistrationExpirationTime(clock.now().plus(Duration.ofDays(365)))
|
||||
.setCreationTime(clock.now().minus(Duration.ofDays(365)))
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setRepoId("9999-EXAMPLE")
|
||||
.build());
|
||||
doSuccessfulTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_recentlyDeletedDomain_beforeXapStarts_notOptedIn_succeedsWithoutXapFee()
|
||||
throws Exception {
|
||||
// If a domain was recently deleted but XAP mode is currently disabled on the TLD, the domain
|
||||
// can be registered at standard pricing without XAP fees or reservation blocks by
|
||||
// non-opted-in registrars.
|
||||
persistHosts();
|
||||
Instant deletionTime = clock.now().minus(Duration.ofHours(1));
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.DISABLED))
|
||||
.build());
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(false)
|
||||
.build());
|
||||
persistResource(
|
||||
new Domain.Builder()
|
||||
.setDomainName(getUniqueIdFromCommand())
|
||||
.setDeletionTime(deletionTime)
|
||||
.setRegistrationExpirationTime(clock.now().plus(Duration.ofDays(365)))
|
||||
.setCreationTime(clock.now().minus(Duration.ofDays(365)))
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setRepoId("9999-EXAMPLE")
|
||||
.build());
|
||||
doSuccessfulTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_recentlyDeletedDomain_beforeXapStarts_optedIn_succeedsWithoutXapFee()
|
||||
throws Exception {
|
||||
// If a domain was recently deleted but XAP mode is currently disabled on the TLD, the domain
|
||||
// can be registered at standard pricing without XAP fees by opted-in registrars.
|
||||
persistHosts();
|
||||
Instant deletionTime = clock.now().minus(Duration.ofHours(1));
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.DISABLED))
|
||||
.build());
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(true)
|
||||
.build());
|
||||
persistResource(
|
||||
new Domain.Builder()
|
||||
.setDomainName(getUniqueIdFromCommand())
|
||||
.setDeletionTime(deletionTime)
|
||||
.setRegistrationExpirationTime(clock.now().plus(Duration.ofDays(365)))
|
||||
.setCreationTime(clock.now().minus(Duration.ofDays(365)))
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setRepoId("9999-EXAMPLE")
|
||||
.build());
|
||||
doSuccessfulTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_agpDeletedRegularDomain_duringXap_succeedsWithoutXapFee() throws Exception {
|
||||
// A regular domain (registered without an XAP fee) that was deleted during its Add Grace
|
||||
// Period (AGP) is exempt from XAP, succeeding at standard pricing without reservation blocks
|
||||
// or XAP fees when re-registered during active XAP on the TLD.
|
||||
persistHosts();
|
||||
Instant creationTime = clock.now().minus(Duration.ofDays(2));
|
||||
Instant deletionTime = clock.now().minus(Duration.ofHours(1));
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(
|
||||
START_INSTANT,
|
||||
Tld.ExpiryAccessPeriodMode.DISABLED,
|
||||
clock.now().minus(Duration.ofHours(1)),
|
||||
Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(false)
|
||||
.build());
|
||||
persistResource(
|
||||
new Domain.Builder()
|
||||
.setDomainName(getUniqueIdFromCommand())
|
||||
.setCreationTime(creationTime)
|
||||
.setDeletionTime(deletionTime)
|
||||
.setRegistrationExpirationTime(clock.now().plus(Duration.ofDays(365)))
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setRepoId("9999-EXAMPLE")
|
||||
.build());
|
||||
doSuccessfulTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_agpDeletedXapDomain_duringXap_succeedsWithoutXapFee() throws Exception {
|
||||
// A domain originally registered with an XAP fee that was deleted during its Add Grace
|
||||
// Period (AGP) is exempt from XAP upon subsequent re-registration, succeeding at standard
|
||||
// pricing without reservation blocks or XAP fees when re-registered during active XAP on the
|
||||
// TLD.
|
||||
persistHosts();
|
||||
Instant creationTime = clock.now().minus(Duration.ofHours(2));
|
||||
Instant deletionTime = clock.now().minus(Duration.ofHours(1));
|
||||
persistResource(
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(false)
|
||||
.build());
|
||||
persistResource(
|
||||
new Domain.Builder()
|
||||
.setDomainName(getUniqueIdFromCommand())
|
||||
.setCreationTime(creationTime)
|
||||
.setDeletionTime(deletionTime)
|
||||
.setRegistrationExpirationTime(clock.now().plus(Duration.ofDays(365)))
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setRepoId("9999-EXAMPLE")
|
||||
.build());
|
||||
doSuccessfulTest();
|
||||
}
|
||||
|
||||
private void setXapForTld(String tldStr, Instant deletionTime) throws Exception {
|
||||
persistResource(
|
||||
Registrar.loadByRegistrarId("TheRegistrar")
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodEnabled(true)
|
||||
.build());
|
||||
persistResource(
|
||||
Tld.get(tldStr)
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
persistResource(
|
||||
new Domain.Builder()
|
||||
.setDomainName(getUniqueIdFromCommand())
|
||||
.setDeletionTime(deletionTime)
|
||||
.setRegistrationExpirationTime(clock.now().plus(Duration.ofDays(365)))
|
||||
.setCreationTime(clock.now().minus(Duration.ofDays(365)))
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setRepoId("9999-EXAMPLE")
|
||||
.build());
|
||||
}
|
||||
|
||||
private void setEapForTld(String tld) {
|
||||
persistResource(
|
||||
Tld.get(tld)
|
||||
|
||||
@@ -402,6 +402,36 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
GracePeriodStatus.ADD, "domain_delete_response_fee.xml", FEE_STD_1_0_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_addGracePeriodDelete_doesNotRefundXapFee() throws Exception {
|
||||
setUpSuccessfulTest();
|
||||
BillingEvent createBillingEvent =
|
||||
persistResource(createBillingEvent(Reason.CREATE, Money.of(USD, 123)));
|
||||
BillingEvent xapBillingEvent =
|
||||
persistResource(createBillingEvent(Reason.FEE_EXPIRY_ACCESS, Money.of(USD, 100)));
|
||||
setUpGracePeriods(
|
||||
GracePeriod.forBillingEvent(GracePeriodStatus.ADD, domain.getRepoId(), createBillingEvent));
|
||||
assertPollMessages(createAutorenewPollMessage("TheRegistrar").build());
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_fee.xml", FEE_STD_1_0_MAP));
|
||||
assertThat(reloadResourceByForeignKey()).isNull();
|
||||
DomainHistory historyEntryDomainDelete =
|
||||
getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE, DomainHistory.class);
|
||||
assertBillingEvents(
|
||||
createAutorenewBillingEvent("TheRegistrar").setRecurrenceEndTime(clock.now()).build(),
|
||||
createBillingEvent,
|
||||
xapBillingEvent,
|
||||
new BillingCancellation.Builder()
|
||||
.setReason(Reason.CREATE)
|
||||
.setTargetId("example.tld")
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setEventTime(clock.now())
|
||||
.setBillingTime(plusDays(TIME_BEFORE_FLOW, 1))
|
||||
.setBillingEvent(createBillingEvent.createVKey())
|
||||
.setDomainHistory(historyEntryDomainDelete)
|
||||
.build());
|
||||
}
|
||||
|
||||
private void doSuccessfulTest_noAddGracePeriod(String responseFilename) throws Exception {
|
||||
doSuccessfulTest_noAddGracePeriod(responseFilename, ImmutableMap.of());
|
||||
}
|
||||
|
||||
@@ -30,12 +30,15 @@ import static google.registry.util.DateTimeUtils.END_INSTANT;
|
||||
import static google.registry.util.DateTimeUtils.START_INSTANT;
|
||||
import static google.registry.util.DateTimeUtils.minusHours;
|
||||
import static google.registry.util.DateTimeUtils.plusHours;
|
||||
import static org.joda.money.CurrencyUnit.JPY;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Range;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.HttpSessionMetadata;
|
||||
import google.registry.flows.SessionMetadata;
|
||||
@@ -58,8 +61,10 @@ import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeHttpSession;
|
||||
import google.registry.util.Clock;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -85,7 +90,12 @@ public class DomainPricingLogicTest {
|
||||
createTld("example");
|
||||
sessionMetadata = new HttpSessionMetadata(new FakeHttpSession());
|
||||
domainPricingLogic =
|
||||
new DomainPricingLogic(new DomainPricingCustomLogic(eppInput, sessionMetadata, null));
|
||||
new DomainPricingLogic(
|
||||
new DomainPricingCustomLogic(eppInput, sessionMetadata, null),
|
||||
Duration.ofDays(10),
|
||||
Duration.ofHours(1),
|
||||
ImmutableMap.of(USD, new BigDecimal("100000.00"), JPY, new BigDecimal("10000000")),
|
||||
ImmutableMap.of(USD, new BigDecimal("10.00"), JPY, new BigDecimal("1000")));
|
||||
tld =
|
||||
persistResource(
|
||||
Tld.get("example")
|
||||
@@ -146,7 +156,14 @@ public class DomainPricingLogicTest {
|
||||
persistResource(Tld.get("sunrise").asBuilder().setTldStateTransitions(transitions).build());
|
||||
assertThat(
|
||||
domainPricingLogic.getCreatePrice(
|
||||
sunriseTld, "domain.sunrise", clock.now(), 2, false, true, Optional.empty()))
|
||||
sunriseTld,
|
||||
"domain.sunrise",
|
||||
clock.now(),
|
||||
Optional.empty(),
|
||||
2,
|
||||
false,
|
||||
true,
|
||||
Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -170,7 +187,14 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getCreatePrice(
|
||||
tld, "default.example", clock.now(), 1, false, false, Optional.of(allocationToken)))
|
||||
tld,
|
||||
"default.example",
|
||||
clock.now(),
|
||||
Optional.empty(),
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -195,7 +219,14 @@ public class DomainPricingLogicTest {
|
||||
// 3 year create should be 5 (discount price) + 10*2 (regular price) = 25.
|
||||
assertThat(
|
||||
domainPricingLogic.getCreatePrice(
|
||||
tld, "default.example", clock.now(), 3, false, false, Optional.of(allocationToken)))
|
||||
tld,
|
||||
"default.example",
|
||||
clock.now(),
|
||||
Optional.empty(),
|
||||
3,
|
||||
false,
|
||||
false,
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -218,7 +249,14 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getCreatePrice(
|
||||
tld, "default.example", clock.now(), 1, false, false, Optional.of(allocationToken)))
|
||||
tld,
|
||||
"default.example",
|
||||
clock.now(),
|
||||
Optional.empty(),
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -1006,7 +1044,14 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getCreatePrice(
|
||||
tld, "premium.example", clock.now(), 1, false, false, Optional.of(allocationToken)))
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.now(),
|
||||
Optional.empty(),
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -1015,7 +1060,14 @@ public class DomainPricingLogicTest {
|
||||
// Two-year create should be 13 (standard price) + 100 (premium price), and it's premium
|
||||
assertThat(
|
||||
domainPricingLogic.getCreatePrice(
|
||||
tld, "premium.example", clock.now(), 2, false, false, Optional.of(allocationToken)))
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.now(),
|
||||
Optional.empty(),
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -1051,7 +1103,14 @@ public class DomainPricingLogicTest {
|
||||
// are standard
|
||||
assertThat(
|
||||
domainPricingLogic.getCreatePrice(
|
||||
tld, "premium.example", clock.now(), 2, false, false, Optional.of(allocationToken)))
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.now(),
|
||||
Optional.empty(),
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -1060,7 +1119,14 @@ public class DomainPricingLogicTest {
|
||||
// Similarly, 3 years should be 13 + 10 + 10
|
||||
assertThat(
|
||||
domainPricingLogic.getCreatePrice(
|
||||
tld, "premium.example", clock.now(), 3, false, false, Optional.of(allocationToken)))
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.now(),
|
||||
Optional.empty(),
|
||||
3,
|
||||
false,
|
||||
false,
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -1081,7 +1147,14 @@ public class DomainPricingLogicTest {
|
||||
// Two-year create should be 100 (premium 1st year) plus 10 (nonpremium 2nd year)
|
||||
assertThat(
|
||||
domainPricingLogic.getCreatePrice(
|
||||
tld, "premium.example", clock.now(), 2, false, false, Optional.of(allocationToken)))
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.now(),
|
||||
Optional.empty(),
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -1090,7 +1163,14 @@ public class DomainPricingLogicTest {
|
||||
// Similarly, 3 years should be 100 + 10 + 10
|
||||
assertThat(
|
||||
domainPricingLogic.getCreatePrice(
|
||||
tld, "premium.example", clock.now(), 3, false, false, Optional.of(allocationToken)))
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.now(),
|
||||
Optional.empty(),
|
||||
3,
|
||||
false,
|
||||
false,
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -1135,4 +1215,291 @@ public class DomainPricingLogicTest {
|
||||
.getRenewCost())
|
||||
.isEqualTo(Money.of(USD, 25));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetXapFeeFor_tier0_startOfXap() {
|
||||
Instant deletionTime = clock.now();
|
||||
Instant checkTime = deletionTime.plus(Duration.ofMinutes(30));
|
||||
Optional<Fee> xapFee = domainPricingLogic.getXapFeeFor(checkTime, deletionTime, USD);
|
||||
assertThat(xapFee)
|
||||
.hasValue(
|
||||
Fee.create(
|
||||
new BigDecimal("100000.00"),
|
||||
Fee.FeeType.XAP,
|
||||
false,
|
||||
Range.closedOpen(deletionTime, deletionTime.plus(Duration.ofHours(1))),
|
||||
deletionTime.plus(Duration.ofHours(1))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetXapFeeFor_tier1() {
|
||||
Instant deletionTime = clock.now();
|
||||
Instant checkTime = deletionTime.plus(Duration.ofHours(1)).plus(Duration.ofMinutes(15));
|
||||
Optional<Fee> xapFee = domainPricingLogic.getXapFeeFor(checkTime, deletionTime, USD);
|
||||
assertThat(xapFee)
|
||||
.hasValue(
|
||||
Fee.create(
|
||||
new BigDecimal("96219.61"),
|
||||
Fee.FeeType.XAP,
|
||||
false,
|
||||
Range.closedOpen(
|
||||
deletionTime.plus(Duration.ofHours(1)), deletionTime.plus(Duration.ofHours(2))),
|
||||
deletionTime.plus(Duration.ofHours(2))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetXapFeeFor_tier239_finalTier() {
|
||||
Instant deletionTime = clock.now();
|
||||
Instant checkTime = deletionTime.plus(Duration.ofHours(239)).plus(Duration.ofMinutes(30));
|
||||
Optional<Fee> xapFee = domainPricingLogic.getXapFeeFor(checkTime, deletionTime, USD);
|
||||
assertThat(xapFee)
|
||||
.hasValue(
|
||||
Fee.create(
|
||||
new BigDecimal("10.00"),
|
||||
Fee.FeeType.XAP,
|
||||
false,
|
||||
Range.closedOpen(
|
||||
deletionTime.plus(Duration.ofHours(239)),
|
||||
deletionTime.plus(Duration.ofHours(240))),
|
||||
deletionTime.plus(Duration.ofHours(240))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetXapFeeFor_unconfiguredCurrency() {
|
||||
Instant deletionTime = clock.now();
|
||||
Instant checkTime = deletionTime.plus(Duration.ofMinutes(30));
|
||||
Optional<Fee> xapFee =
|
||||
domainPricingLogic.getXapFeeFor(checkTime, deletionTime, CurrencyUnit.EUR);
|
||||
assertThat(xapFee).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetCreatePrice_xapEnabled_includesXapFeeAndRequiresExtension() throws Exception {
|
||||
Tld xapTld =
|
||||
persistResource(
|
||||
tld.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
Instant deletionTime = clock.now().minus(Duration.ofHours(1));
|
||||
Domain deletedDomain =
|
||||
new Domain.Builder()
|
||||
.setDomainName("deleted.example")
|
||||
.setDeletionTime(deletionTime)
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setRepoId("2-EXAMPLE")
|
||||
.build();
|
||||
FeesAndCredits feesAndCredits =
|
||||
domainPricingLogic.getCreatePrice(
|
||||
xapTld,
|
||||
"deleted.example",
|
||||
clock.now(),
|
||||
Optional.of(deletedDomain),
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
Optional.empty());
|
||||
assertThat(feesAndCredits.isFeeExtensionRequired()).isTrue();
|
||||
assertThat(feesAndCredits.getXapCost()).isEqualTo(Money.of(USD, new BigDecimal("96219.61")));
|
||||
assertThat(feesAndCredits.getTotalCost()).isEqualTo(Money.of(USD, new BigDecimal("96232.61")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetXapFeeFor_afterXapEnds_returnsEmpty() {
|
||||
Instant deletionTime = clock.now();
|
||||
Instant checkTime = deletionTime.plus(Duration.ofDays(10)).plus(Duration.ofMinutes(1));
|
||||
Optional<Fee> xapFee = domainPricingLogic.getXapFeeFor(checkTime, deletionTime, USD);
|
||||
assertThat(xapFee).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetCreatePrice_agpDeletedRegularDomain_duringXap_noXapFee() throws Exception {
|
||||
// A regular domain deleted during AGP is exempt from XAP fee evaluation upon subsequent
|
||||
// creation during active XAP.
|
||||
Tld xapTld =
|
||||
persistResource(
|
||||
tld.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(
|
||||
START_INSTANT,
|
||||
Tld.ExpiryAccessPeriodMode.DISABLED,
|
||||
clock.now().minus(Duration.ofHours(1)),
|
||||
Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
Instant creationTime = clock.now().minus(Duration.ofDays(2));
|
||||
Instant deletionTime = clock.now().minus(Duration.ofHours(1));
|
||||
Domain deletedDomain =
|
||||
new Domain.Builder()
|
||||
.setDomainName("deleted.example")
|
||||
.setCreationTime(creationTime)
|
||||
.setDeletionTime(deletionTime)
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setRepoId("2-EXAMPLE")
|
||||
.build();
|
||||
FeesAndCredits feesAndCredits =
|
||||
domainPricingLogic.getCreatePrice(
|
||||
xapTld,
|
||||
"deleted.example",
|
||||
clock.now(),
|
||||
Optional.of(deletedDomain),
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
Optional.empty());
|
||||
assertThat(feesAndCredits.isFeeExtensionRequired()).isFalse();
|
||||
assertThat(feesAndCredits.getXapCost()).isEqualTo(Money.zero(USD));
|
||||
assertThat(feesAndCredits.getTotalCost()).isEqualTo(Money.of(USD, new BigDecimal("13.00")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetCreatePrice_agpDeletedXapDomain_duringXap_noXapFee() throws Exception {
|
||||
// A domain originally registered with an XAP fee deleted during AGP is exempt from XAP fee
|
||||
// evaluation upon subsequent creation during active XAP.
|
||||
Tld xapTld =
|
||||
persistResource(
|
||||
tld.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
Instant creationTime = clock.now().minus(Duration.ofHours(2));
|
||||
Instant deletionTime = clock.now().minus(Duration.ofHours(1));
|
||||
Domain deletedDomain =
|
||||
new Domain.Builder()
|
||||
.setDomainName("deleted.example")
|
||||
.setCreationTime(creationTime)
|
||||
.setDeletionTime(deletionTime)
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setRepoId("2-EXAMPLE")
|
||||
.build();
|
||||
FeesAndCredits feesAndCredits =
|
||||
domainPricingLogic.getCreatePrice(
|
||||
xapTld,
|
||||
"deleted.example",
|
||||
clock.now(),
|
||||
Optional.of(deletedDomain),
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
Optional.empty());
|
||||
assertThat(feesAndCredits.isFeeExtensionRequired()).isFalse();
|
||||
assertThat(feesAndCredits.getXapCost()).isEqualTo(Money.zero(USD));
|
||||
assertThat(feesAndCredits.getTotalCost()).isEqualTo(Money.of(USD, new BigDecimal("13.00")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetCreatePrice_anchorTenantDeletedAfterStandardAgp_duringXap_chargesXapFee()
|
||||
throws Exception {
|
||||
// An anchor tenant domain deleted after standard AGP (5 days), e.g. on day 10 of its 30-day
|
||||
// anchor tenant AGP, is subject to XAP fees upon re-registration (not exempt as an AGP delete).
|
||||
Tld xapTld =
|
||||
persistResource(
|
||||
tld.asBuilder()
|
||||
.setAddGracePeriodLength(Duration.ofDays(5))
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
Instant creationTime = clock.now().minus(Duration.ofDays(10));
|
||||
Instant deletionTime = clock.now().minus(Duration.ofHours(1));
|
||||
Domain deletedDomain =
|
||||
new Domain.Builder()
|
||||
.setDomainName("deleted.example")
|
||||
.setCreationTime(creationTime)
|
||||
.setDeletionTime(deletionTime)
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setRepoId("2-EXAMPLE")
|
||||
.build();
|
||||
FeesAndCredits feesAndCredits =
|
||||
domainPricingLogic.getCreatePrice(
|
||||
xapTld,
|
||||
"deleted.example",
|
||||
clock.now(),
|
||||
Optional.of(deletedDomain),
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
Optional.empty());
|
||||
assertThat(feesAndCredits.isFeeExtensionRequired()).isTrue();
|
||||
assertThat(feesAndCredits.getXapCost()).isEqualTo(Money.of(USD, new BigDecimal("96219.61")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetCreatePrice_premiumDomainInXap_chargesPremiumAndXapFees() throws Exception {
|
||||
// Calculating create price for a recently deleted premium domain during active XAP should sum
|
||||
// both the premium create fee and the one-time XAP tier fee.
|
||||
Tld xapTld =
|
||||
persistResource(
|
||||
tld.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
Instant deletionTime = clock.now().minus(Duration.ofHours(1));
|
||||
Domain deletedDomain =
|
||||
new Domain.Builder()
|
||||
.setDomainName("premium.example")
|
||||
.setDeletionTime(deletionTime)
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setRepoId("2-EXAMPLE")
|
||||
.build();
|
||||
FeesAndCredits feesAndCredits =
|
||||
domainPricingLogic.getCreatePrice(
|
||||
xapTld,
|
||||
"premium.example",
|
||||
clock.now(),
|
||||
Optional.of(deletedDomain),
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
Optional.empty());
|
||||
assertThat(feesAndCredits.hasAnyPremiumFees()).isTrue();
|
||||
assertThat(feesAndCredits.isFeeExtensionRequired()).isTrue();
|
||||
assertThat(feesAndCredits.getCreateCost()).isEqualTo(Money.of(USD, new BigDecimal("100.00")));
|
||||
assertThat(feesAndCredits.getXapCost()).isEqualTo(Money.of(USD, new BigDecimal("96219.61")));
|
||||
assertThat(feesAndCredits.getTotalCost()).isEqualTo(Money.of(USD, new BigDecimal("96319.61")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetCreatePrice_zeroXapFee_doesNotRequireExtension() throws Exception {
|
||||
// When an XAP tier fee is $0.00, it is included in the fee items but does not require a fee
|
||||
// extension acknowledgment from the registrar.
|
||||
Tld xapTld =
|
||||
persistResource(
|
||||
tld.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Tld.ExpiryAccessPeriodMode.ENABLED))
|
||||
.build());
|
||||
DomainPricingLogic zeroFeePricingLogic =
|
||||
new DomainPricingLogic(
|
||||
new DomainPricingCustomLogic(eppInput, sessionMetadata, null),
|
||||
Duration.ofDays(10),
|
||||
Duration.ofHours(1),
|
||||
ImmutableMap.of(USD, BigDecimal.ZERO),
|
||||
ImmutableMap.of(USD, BigDecimal.ZERO));
|
||||
Instant deletionTime = clock.now().minus(Duration.ofHours(1));
|
||||
Domain deletedDomain =
|
||||
new Domain.Builder()
|
||||
.setDomainName("deleted.example")
|
||||
.setDeletionTime(deletionTime)
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setRepoId("2-EXAMPLE")
|
||||
.build();
|
||||
FeesAndCredits feesAndCredits =
|
||||
zeroFeePricingLogic.getCreatePrice(
|
||||
xapTld,
|
||||
"deleted.example",
|
||||
clock.now(),
|
||||
Optional.of(deletedDomain),
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
Optional.empty());
|
||||
assertThat(feesAndCredits.isFeeExtensionRequired()).isFalse();
|
||||
assertThat(feesAndCredits.getXapCost()).isEqualTo(Money.zero(USD));
|
||||
assertThat(feesAndCredits.getTotalCost()).isEqualTo(Money.of(USD, new BigDecimal("13.00")));
|
||||
}
|
||||
}
|
||||
|
||||
+94
-44
@@ -22,6 +22,7 @@ import static google.registry.model.domain.token.AllocationToken.TokenStatus.VAL
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
|
||||
@@ -36,6 +37,7 @@ import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
@@ -44,6 +46,7 @@ import google.registry.flows.custom.DomainPricingCustomLogic;
|
||||
import google.registry.flows.domain.DomainPricingLogic;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.NonexistentAllocationTokenException;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
@@ -54,6 +57,8 @@ import google.registry.model.tld.Tld;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.TestCacheExtension;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -69,11 +74,20 @@ class AllocationTokenFlowUtilsTest {
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
|
||||
@RegisterExtension
|
||||
final TestCacheExtension testCacheExtension =
|
||||
new TestCacheExtension.Builder().withAllocationTokenCache(Duration.ofMinutes(10)).build();
|
||||
|
||||
private final AllocationTokenExtension allocationTokenExtension =
|
||||
mock(AllocationTokenExtension.class);
|
||||
|
||||
private final DomainPricingLogic domainPricingLogic =
|
||||
new DomainPricingLogic(new DomainPricingCustomLogic(null, null, null));
|
||||
new DomainPricingLogic(
|
||||
new DomainPricingCustomLogic(null, null, null),
|
||||
Duration.ofDays(30),
|
||||
Duration.ofDays(1),
|
||||
ImmutableMap.of(),
|
||||
ImmutableMap.of());
|
||||
|
||||
private Tld tld;
|
||||
|
||||
@@ -124,8 +138,13 @@ class AllocationTokenFlowUtilsTest {
|
||||
.build());
|
||||
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
|
||||
assertThat(
|
||||
AllocationTokenFlowUtils.loadAllocationTokenFromExtension(
|
||||
"TheRegistrar", "example.tld", clock.now(), Optional.of(allocationTokenExtension)))
|
||||
tm().transact(
|
||||
() ->
|
||||
AllocationTokenFlowUtils.loadAllocationTokenFromExtension(
|
||||
"TheRegistrar",
|
||||
"example.tld",
|
||||
clock.now(),
|
||||
Optional.of(allocationTokenExtension))))
|
||||
.hasValue(token);
|
||||
}
|
||||
|
||||
@@ -141,15 +160,17 @@ class AllocationTokenFlowUtilsTest {
|
||||
.build());
|
||||
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
|
||||
assertThat(
|
||||
AllocationTokenFlowUtils.loadTokenFromExtensionOrGetDefault(
|
||||
"TheRegistrar",
|
||||
clock.now(),
|
||||
Optional.of(allocationTokenExtension),
|
||||
tld,
|
||||
"example.tld",
|
||||
CommandName.CREATE,
|
||||
Optional.of(1),
|
||||
domainPricingLogic))
|
||||
tm().transact(
|
||||
() ->
|
||||
AllocationTokenFlowUtils.loadTokenFromExtensionOrGetDefault(
|
||||
"TheRegistrar",
|
||||
clock.now(),
|
||||
Optional.of(allocationTokenExtension),
|
||||
tld,
|
||||
"example.tld",
|
||||
CommandName.CREATE,
|
||||
Optional.of(1),
|
||||
domainPricingLogic)))
|
||||
.hasValue(token);
|
||||
}
|
||||
|
||||
@@ -181,15 +202,17 @@ class AllocationTokenFlowUtilsTest {
|
||||
.build());
|
||||
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
|
||||
assertThat(
|
||||
AllocationTokenFlowUtils.loadTokenFromExtensionOrGetDefault(
|
||||
"TheRegistrar",
|
||||
clock.now(),
|
||||
Optional.of(allocationTokenExtension),
|
||||
tld,
|
||||
"example.tld",
|
||||
CommandName.CREATE,
|
||||
Optional.of(1),
|
||||
domainPricingLogic))
|
||||
tm().transact(
|
||||
() ->
|
||||
AllocationTokenFlowUtils.loadTokenFromExtensionOrGetDefault(
|
||||
"TheRegistrar",
|
||||
clock.now(),
|
||||
Optional.of(allocationTokenExtension),
|
||||
tld,
|
||||
"example.tld",
|
||||
CommandName.CREATE,
|
||||
Optional.of(1),
|
||||
domainPricingLogic)))
|
||||
.hasValue(defaultToken);
|
||||
}
|
||||
|
||||
@@ -261,6 +284,29 @@ class AllocationTokenFlowUtilsTest {
|
||||
assertLoadTokenFromExtensionThrowsException(NonexistentAllocationTokenException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_loadFromExtension_alreadyRedeemedToken() {
|
||||
persistResource(
|
||||
singleUseTokenBuilder().setRedemptionHistoryId(new HistoryEntryId("repoId", 1L)).build());
|
||||
assertLoadTokenFromExtensionThrowsException(AlreadyRedeemedAllocationTokenException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_loadFromExtension_singleUseTokenStaleCacheReloadsFromDatabase() {
|
||||
AllocationToken token = persistResource(singleUseTokenBuilder().build());
|
||||
assertThat(AllocationToken.get(token.createVKey())).hasValue(token);
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().put(
|
||||
token
|
||||
.asBuilder()
|
||||
.setRedemptionHistoryId(new HistoryEntryId("repoId", 1L))
|
||||
.build()));
|
||||
// cache still returns the old un-redeemed token
|
||||
assertThat(AllocationToken.get(token.createVKey()).get().isRedeemed()).isFalse();
|
||||
assertLoadTokenFromExtensionThrowsException(AlreadyRedeemedAllocationTokenException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_tokenInvalidForRegistrar() {
|
||||
persistResource(
|
||||
@@ -334,18 +380,20 @@ class AllocationTokenFlowUtilsTest {
|
||||
// Tokens tied to a domain should throw a catastrophic exception if used for a different domain
|
||||
persistResource(singleUseTokenBuilder().setDomainName("someotherdomain.tld").build());
|
||||
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
|
||||
assertThrows(
|
||||
AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException.class,
|
||||
() ->
|
||||
AllocationTokenFlowUtils.loadTokenFromExtensionOrGetDefault(
|
||||
"TheRegistrar",
|
||||
clock.now(),
|
||||
Optional.of(allocationTokenExtension),
|
||||
tld,
|
||||
"example.tld",
|
||||
CommandName.CREATE,
|
||||
Optional.of(1),
|
||||
domainPricingLogic));
|
||||
tm().transact(
|
||||
() ->
|
||||
assertThrows(
|
||||
AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException.class,
|
||||
() ->
|
||||
AllocationTokenFlowUtils.loadTokenFromExtensionOrGetDefault(
|
||||
"TheRegistrar",
|
||||
clock.now(),
|
||||
Optional.of(allocationTokenExtension),
|
||||
tld,
|
||||
"example.tld",
|
||||
CommandName.CREATE,
|
||||
Optional.of(1),
|
||||
domainPricingLogic)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -451,17 +499,19 @@ class AllocationTokenFlowUtilsTest {
|
||||
}
|
||||
|
||||
private void assertLoadTokenFromExtensionThrowsException(Class<? extends EppException> clazz) {
|
||||
assertAboutEppExceptions()
|
||||
.that(
|
||||
assertThrows(
|
||||
clazz,
|
||||
() ->
|
||||
AllocationTokenFlowUtils.loadAllocationTokenFromExtension(
|
||||
"TheRegistrar",
|
||||
"example.tld",
|
||||
clock.now(),
|
||||
Optional.of(allocationTokenExtension))))
|
||||
.marshalsToXml();
|
||||
tm().transact(
|
||||
() ->
|
||||
assertAboutEppExceptions()
|
||||
.that(
|
||||
assertThrows(
|
||||
clazz,
|
||||
() ->
|
||||
AllocationTokenFlowUtils.loadAllocationTokenFromExtension(
|
||||
"TheRegistrar",
|
||||
"example.tld",
|
||||
tm().getTxTime(),
|
||||
Optional.of(allocationTokenExtension))))
|
||||
.marshalsToXml());
|
||||
}
|
||||
|
||||
private AllocationToken.Builder singleUseTokenBuilder() {
|
||||
|
||||
@@ -144,4 +144,17 @@ class ForeignKeyUtilsTest {
|
||||
fakeClock.now()))
|
||||
.containsExactlyEntriesIn(ImmutableMap.of("ns1.example.com", host1.createVKey()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_loadResourcesByCache_skipsDeletedAndNonexistent() {
|
||||
Host host1 = persistActiveHost("ns1.example.com");
|
||||
Host host2 = persistActiveHost("ns2.example.com");
|
||||
persistResource(host2.asBuilder().setDeletionTime(minusDays(fakeClock.now(), 1)).build());
|
||||
assertThat(
|
||||
ForeignKeyUtils.loadResourcesByCache(
|
||||
Host.class,
|
||||
ImmutableList.of("ns1.example.com", "ns2.example.com", "ns3.example.com"),
|
||||
fakeClock.now()))
|
||||
.containsExactlyEntriesIn(ImmutableMap.of("ns1.example.com", host1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,4 +773,20 @@ class RegistrarTest extends EntityTestCase {
|
||||
assertThat(Registrar.loadByRegistrarId("registrar").toString())
|
||||
.contains("allowedTlds=[bar, baz, foo, gon, tri]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_expiryAccessPeriodEnabled() {
|
||||
assertThat(registrar.getExpiryAccessPeriodEnabled()).isFalse();
|
||||
persistResource(registrar.asBuilder().setExpiryAccessPeriodEnabled(true).build());
|
||||
assertThat(
|
||||
Registrar.loadByRegistrarId("registrar").orElseThrow().getExpiryAccessPeriodEnabled())
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_toJsonMap_includesExpiryAccessPeriodEnabled() {
|
||||
assertThat(registrar.toJsonMap()).containsEntry("expiryAccessPeriodEnabled", false);
|
||||
Registrar modified = registrar.asBuilder().setExpiryAccessPeriodEnabled(true).build();
|
||||
assertThat(modified.toJsonMap()).containsEntry("expiryAccessPeriodEnabled", true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +257,8 @@ public final class TldTest extends EntityTestCase {
|
||||
.asBuilder()
|
||||
.setCreateBillingCostTransitions(createCostTransitions)
|
||||
.build());
|
||||
assertThat(thrown.getMessage()).isEqualTo("Create billing cost cannot be negative");
|
||||
assertThat(thrown.getMessage())
|
||||
.isEqualTo("Some create cost(s) are negative or in the wrong currency");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -614,8 +615,11 @@ public final class TldTest extends EntityTestCase {
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setRenewBillingCostTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Money.of(USD, -42))));
|
||||
assertThat(thrown).hasMessageThat().contains("billing cost cannot be negative");
|
||||
ImmutableSortedMap.of(START_INSTANT, Money.of(USD, -42)))
|
||||
.build());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Some renew cost(s) are negative or in the wrong currency");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -623,8 +627,10 @@ public final class TldTest extends EntityTestCase {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, -42)));
|
||||
assertThat(thrown).hasMessageThat().contains("restoreBillingCost cannot be negative");
|
||||
() -> Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, -42)).build());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Restore cost is negative or in the wrong currency");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -652,8 +658,14 @@ public final class TldTest extends EntityTestCase {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> Tld.get("tld").asBuilder().setServerStatusChangeBillingCost(Money.of(USD, -42)));
|
||||
assertThat(thrown).hasMessageThat().contains("billing cost cannot be negative");
|
||||
() ->
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setServerStatusChangeBillingCost(Money.of(USD, -42))
|
||||
.build());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Server status change cost is negative or in the wrong currency");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -667,7 +679,9 @@ public final class TldTest extends EntityTestCase {
|
||||
.setRenewBillingCostTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Money.of(EUR, 42)))
|
||||
.build());
|
||||
assertThat(thrown).hasMessageThat().contains("cost must be in the TLD's currency");
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Some renew cost(s) are negative or in the wrong currency");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -681,7 +695,7 @@ public final class TldTest extends EntityTestCase {
|
||||
.setCreateBillingCostTransitions(
|
||||
ImmutableSortedMap.of(START_INSTANT, Money.of(EUR, 42)))
|
||||
.build());
|
||||
assertThat(thrown).hasMessageThat().contains("cost must be in the TLD's currency");
|
||||
assertThat(thrown).hasMessageThat().contains("negative or in the wrong currency");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -690,7 +704,7 @@ public final class TldTest extends EntityTestCase {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(EUR, 42)).build());
|
||||
assertThat(thrown).hasMessageThat().contains("cost must be in the TLD's currency");
|
||||
assertThat(thrown).hasMessageThat().contains("negative or in the wrong currency");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -703,7 +717,7 @@ public final class TldTest extends EntityTestCase {
|
||||
.asBuilder()
|
||||
.setServerStatusChangeBillingCost(Money.of(EUR, 42))
|
||||
.build());
|
||||
assertThat(thrown).hasMessageThat().contains("cost must be in the TLD's currency");
|
||||
assertThat(thrown).hasMessageThat().contains("negative or in the wrong currency");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -732,6 +746,37 @@ public final class TldTest extends EntityTestCase {
|
||||
.isEqualTo(new BigDecimal("50.00"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExpiryAccessPeriodMode_undefined() {
|
||||
assertThat(Tld.get("tld").getExpiryAccessPeriodModeAt(fakeClock.now()))
|
||||
.isEqualTo(Tld.ExpiryAccessPeriodMode.DISABLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExpiryAccessPeriodMode_specified() {
|
||||
Instant a = minusDays(fakeClock.now(), 1);
|
||||
Instant b = plusDays(fakeClock.now(), 1);
|
||||
Tld tld =
|
||||
Tld.get("tld")
|
||||
.asBuilder()
|
||||
.setExpiryAccessPeriodTransitions(
|
||||
ImmutableSortedMap.of(
|
||||
START_INSTANT,
|
||||
Tld.ExpiryAccessPeriodMode.DISABLED,
|
||||
a,
|
||||
Tld.ExpiryAccessPeriodMode.ENABLED,
|
||||
b,
|
||||
Tld.ExpiryAccessPeriodMode.DISABLED))
|
||||
.build();
|
||||
|
||||
assertThat(tld.getExpiryAccessPeriodModeAt(fakeClock.now()))
|
||||
.isEqualTo(Tld.ExpiryAccessPeriodMode.ENABLED);
|
||||
assertThat(tld.getExpiryAccessPeriodModeAt(minusDays(fakeClock.now(), 2)))
|
||||
.isEqualTo(Tld.ExpiryAccessPeriodMode.DISABLED);
|
||||
assertThat(tld.getExpiryAccessPeriodModeAt(plusDays(fakeClock.now(), 2)))
|
||||
.isEqualTo(Tld.ExpiryAccessPeriodMode.DISABLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_eapFee_wrongCurrency() {
|
||||
IllegalArgumentException thrown =
|
||||
@@ -742,7 +787,7 @@ public final class TldTest extends EntityTestCase {
|
||||
.asBuilder()
|
||||
.setEapFeeSchedule(ImmutableSortedMap.of(START_INSTANT, Money.zero(EUR)))
|
||||
.build());
|
||||
assertThat(thrown).hasMessageThat().contains("All EAP fees must be in the TLD's currency");
|
||||
assertThat(thrown).hasMessageThat().contains("negative or in the wrong currency");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -25,6 +25,7 @@ import static google.registry.util.DateTimeUtils.minusMonths;
|
||||
import static google.registry.util.DateTimeUtils.minusYears;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.rdap.RdapMetrics.EndpointType;
|
||||
import google.registry.rdap.RdapMetrics.SearchType;
|
||||
@@ -111,13 +112,16 @@ class RdapNameserverActionTest extends RdapActionBaseTestCase<RdapNameserverActi
|
||||
@Test
|
||||
void testNameserver_tldTithHyphenOn3And4_works() {
|
||||
createTld("zz--main-2166");
|
||||
persistResource(makePunycodedHost("ns1.cat.zz--main-2166", "1.2.3.4", null, "TheRegistrar"));
|
||||
Host host =
|
||||
persistResource(
|
||||
makePunycodedHost("ns1.cat.zz--main-2166", "1.2.3.4", null, "TheRegistrar"));
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("ns1.cat.zz--main-2166"))
|
||||
.isEqualTo(
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns1.cat.zz--main-2166", "ns1.cat.zz--main-2166", "F-ROID")
|
||||
.addNameserver(
|
||||
"ns1.cat.zz--main-2166", "ns1.cat.zz--main-2166", host.getRepoId())
|
||||
.putAll("ADDRESSTYPE", "v4", "ADDRESS", "1.2.3.4", "STATUS", "active")
|
||||
.load("rdap_host.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
|
||||
@@ -29,6 +29,7 @@ import static google.registry.util.DateTimeUtils.minusDays;
|
||||
import static google.registry.util.DateTimeUtils.minusMonths;
|
||||
import static google.registry.util.DateTimeUtils.minusYears;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
@@ -46,6 +47,7 @@ import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.FullFieldsTestEntityHelper;
|
||||
import java.net.URLDecoder;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -428,7 +430,7 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
|
||||
action.registrarParam = Optional.of("unicoderegistrar");
|
||||
generateActualJsonWithName("ns*.cat.lol");
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
verifyErrorMetrics(Optional.of(2L), 404);
|
||||
verifyErrorMetrics(Optional.of(0L), 404);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -445,6 +447,30 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
|
||||
verifyMetrics(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNameMatch_star_cat_lol_usesForeignKeyCache() {
|
||||
generateActualJsonWithName("*.cat.lol");
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(2);
|
||||
clearInvocations(rdapMetrics);
|
||||
|
||||
Instant newTransferTime = clock.now();
|
||||
persistResource(hostNs1CatLol.asBuilder().setLastTransferTime(newTransferTime).build());
|
||||
clock.advanceOneMilli();
|
||||
|
||||
action.response = new FakeResponse();
|
||||
generateActualJsonWithName("*.cat.lol");
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(2);
|
||||
|
||||
JsonObject searchResults =
|
||||
parseJsonObject(response.getPayload())
|
||||
.getAsJsonArray("nameserverSearchResults")
|
||||
.get(0)
|
||||
.getAsJsonObject();
|
||||
assertThat(searchResults.toString()).doesNotContain(newTransferTime.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNameMatch_star_cat_lol_found_sameRegistrarRequested() {
|
||||
action.registrarParam = Optional.of("TheRegistrar");
|
||||
@@ -458,7 +484,7 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
|
||||
action.registrarParam = Optional.of("unicoderegistrar");
|
||||
generateActualJsonWithName("*.cat.lol");
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
verifyErrorMetrics(Optional.of(2L), 404);
|
||||
verifyErrorMetrics(Optional.of(0L), 404);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -521,7 +547,7 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
|
||||
"rdap_truncated_hosts.json", "QUERY", "name=nsx*.cat.lol&cursor=bnN4NC5jYXQubG9s"));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
// When searching names, we look for additional matches, in case some are not visible.
|
||||
verifyMetrics(9, IncompletenessWarningType.TRUNCATED);
|
||||
verifyMetrics(5, IncompletenessWarningType.TRUNCATED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -536,7 +562,7 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
|
||||
.putAll("ADDRESSTYPE", "v6", "ADDRESS", "bad:f00d:cafe::15:beef")
|
||||
.load("rdap_host_linked.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(2);
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -73,7 +73,10 @@ class RdapTestHelper {
|
||||
"We reserve the right to modify this agreement at any time.");
|
||||
rdapJsonFormatter.rdapTosStaticUrl = "https://www.example.tld/about/rdap/tos.html";
|
||||
rdapJsonFormatter.hostCache =
|
||||
(repoId) -> Optional.ofNullable(EppResource.loadByCache(VKey.create(Host.class, repoId)));
|
||||
(repoId) ->
|
||||
Optional.ofNullable(EppResource.loadByCache(VKey.create(Host.class, repoId)))
|
||||
.filter(host -> clock.now().isBefore(host.getDeletionTime()))
|
||||
.map(host -> (Host) host.cloneProjectedAtTime(clock.now()));
|
||||
return rdapJsonFormatter;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,14 +15,23 @@
|
||||
package google.registry.request;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.request.RequestModule.provideJsonBody;
|
||||
import static google.registry.request.RequestModule.provideJsonPayload;
|
||||
import static google.registry.request.RequestModule.providePayloadAsBytes;
|
||||
import static google.registry.request.RequestModule.providePayloadAsString;
|
||||
import static google.registry.security.JsonHttpTestUtils.createServletInputStream;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.net.MediaType;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.request.HttpException.PayloadTooLargeException;
|
||||
import google.registry.request.HttpException.UnsupportedMediaTypeException;
|
||||
import google.registry.tools.GsonUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link RequestModule}. */
|
||||
@@ -72,4 +81,46 @@ final class RequestModuleTest {
|
||||
UnsupportedMediaTypeException.class,
|
||||
() -> provideJsonPayload(MediaType.JSON_UTF_8.withParameter("omg", "handel"), "{}", GSON));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProvidePayloadAsBytes_contentLengthExceedsLimit_throws413() {
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getContentLengthLong()).thenReturn((long) RequestModule.MAX_PAYLOAD_BYTES + 1);
|
||||
PayloadTooLargeException thrown =
|
||||
assertThrows(PayloadTooLargeException.class, () -> providePayloadAsBytes(req));
|
||||
assertThat(thrown).hasMessageThat().contains("exceeds limit");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProvidePayloadAsBytes_streamExceedsLimit_throws413() throws Exception {
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getContentLengthLong()).thenReturn(-1L);
|
||||
when(req.getInputStream())
|
||||
.thenReturn(createServletInputStream(new byte[RequestModule.MAX_PAYLOAD_BYTES + 1]));
|
||||
PayloadTooLargeException thrown =
|
||||
assertThrows(PayloadTooLargeException.class, () -> providePayloadAsBytes(req));
|
||||
assertThat(thrown).hasMessageThat().contains("exceeds maximum allowed size");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProvidePayloadAsString_invalidCharset_throws415() {
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getCharacterEncoding()).thenReturn("invalid-charset-name");
|
||||
UnsupportedMediaTypeException thrown =
|
||||
assertThrows(
|
||||
UnsupportedMediaTypeException.class,
|
||||
() -> providePayloadAsString("hello".getBytes(UTF_8), req));
|
||||
assertThat(thrown).hasMessageThat().contains("Unsupported charset: invalid-charset-name");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProvideJsonBody_contentLengthExceedsLimit_throws413() {
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getContentLengthLong()).thenReturn((long) RequestModule.MAX_PAYLOAD_BYTES + 1);
|
||||
PayloadTooLargeException thrown =
|
||||
assertThrows(
|
||||
PayloadTooLargeException.class,
|
||||
() -> provideJsonBody(providePayloadAsString(providePayloadAsBytes(req), req), GSON));
|
||||
assertThat(thrown).hasMessageThat().contains("exceeds limit");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
@@ -80,13 +81,13 @@ public class UrlConnectionUtilsTest {
|
||||
"294");
|
||||
String payload =
|
||||
"""
|
||||
--------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r
|
||||
Content-Disposition: form-data; name="lol"; filename="cat"\r
|
||||
Content-Type: text/csv; charset=utf-8\r
|
||||
\r
|
||||
The nice people at the store say hello. ヘ(◕。◕ヘ)\r
|
||||
--------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA--\r
|
||||
""";
|
||||
--------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r
|
||||
Content-Disposition: form-data; name="lol"; filename="cat"\r
|
||||
Content-Type: text/csv; charset=utf-8\r
|
||||
\r
|
||||
The nice people at the store say hello. ヘ(◕。◕ヘ)\r
|
||||
--------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA--\r
|
||||
""";
|
||||
verify(connection).setDoOutput(true);
|
||||
verify(connection).getOutputStream();
|
||||
assertThat(connectionOutputStream.toByteArray()).isEqualTo(payload.getBytes(UTF_8));
|
||||
@@ -124,4 +125,27 @@ public class UrlConnectionUtilsTest {
|
||||
when(connection.getResponseCode()).thenReturn(400);
|
||||
assertThat(UrlConnectionUtils.getResponseBytes(connection)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetResponseBytes_contentLengthExceedsLimit_throwsException() {
|
||||
HttpsURLConnection connection = mock(HttpsURLConnection.class);
|
||||
when(connection.getContentLengthLong())
|
||||
.thenReturn((long) UrlConnectionUtils.MAX_RESPONSE_PAYLOAD_BYTES + 1);
|
||||
IOException thrown =
|
||||
assertThrows(IOException.class, () -> UrlConnectionUtils.getResponseBytes(connection));
|
||||
assertThat(thrown).hasMessageThat().contains("exceeds limit");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetResponseBytes_streamExceedsLimit_throwsException() throws Exception {
|
||||
HttpsURLConnection connection = mock(HttpsURLConnection.class);
|
||||
when(connection.getContentLengthLong()).thenReturn(-1L);
|
||||
when(connection.getResponseCode()).thenReturn(200);
|
||||
when(connection.getInputStream())
|
||||
.thenReturn(
|
||||
new ByteArrayInputStream(new byte[UrlConnectionUtils.MAX_RESPONSE_PAYLOAD_BYTES + 1]));
|
||||
IOException thrown =
|
||||
assertThrows(IOException.class, () -> UrlConnectionUtils.getResponseBytes(connection));
|
||||
assertThat(thrown).hasMessageThat().contains("exceeds maximum allowed size");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,10 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import google.registry.tools.GsonUtils;
|
||||
import jakarta.servlet.ReadListener;
|
||||
import jakarta.servlet.ServletInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Map;
|
||||
@@ -84,4 +87,32 @@ public final class JsonHttpTestUtils {
|
||||
final StringWriter writer) {
|
||||
return memoize(() -> getJsonResponse(writer));
|
||||
}
|
||||
|
||||
public static ServletInputStream createServletInputStream(byte[] data) {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
return new ServletInputStream() {
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return bais.available() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadListener(ReadListener listener) {}
|
||||
|
||||
@Override
|
||||
public int read() {
|
||||
return bais.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) {
|
||||
return bais.read(b, off, len);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.testing;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import google.registry.model.tmch.ClaimsListDao;
|
||||
import java.time.Duration;
|
||||
@@ -80,6 +81,11 @@ public class TestCacheExtension implements BeforeEachCallback, AfterEachCallback
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withAllocationTokenCache(Duration expiry) {
|
||||
cacheHandlers.add(new TestCacheHandler(AllocationToken::setCacheForTest, expiry));
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestCacheExtension build() {
|
||||
return new TestCacheExtension(ImmutableList.copyOf(cacheHandlers));
|
||||
}
|
||||
|
||||
@@ -454,6 +454,16 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
|
||||
+ " unit USD. Found [EUR] currency unit(s) in the renewBillingCostTransitionsMap");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_negativeCreateCost() throws Exception {
|
||||
File tldFile = tmpDir.resolve("negativecost.yaml").toFile();
|
||||
Files.asCharSink(tldFile, UTF_8).write(loadFile(getClass(), "negativecost.yaml"));
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(IllegalArgumentException.class, () -> runCommandForced("--input=" + tldFile));
|
||||
assertThat(thrown.getMessage())
|
||||
.isEqualTo("Some create cost(s) are negative or in the wrong currency");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_emptyStringClearsDefaultPromoTokens() throws Exception {
|
||||
Tld tld = createTld("tld");
|
||||
|
||||
@@ -119,4 +119,11 @@ class CreatePremiumListCommandTest<C extends CreatePremiumListCommand>
|
||||
+ "yet TLD %s does not exist",
|
||||
fileName));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDryRun_doesNotCreateList() throws Exception {
|
||||
runCommandForced(
|
||||
"--name=" + TLD_TEST, "--input=" + premiumTermsPath, "--currency=USD", "--dry_run");
|
||||
assertThat(PremiumListDao.getLatestRevision(TLD_TEST)).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,4 +180,11 @@ class CreateReservedListCommandTest
|
||||
command.init();
|
||||
assertThat(command.prompt()).contains("reservedListMap=[]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDryRun_doesNotCreateList() throws Exception {
|
||||
runCommandForced(
|
||||
"--name=xn--q9jyb4c_common-reserved", "--input=" + reservedTermsPath, "--dry_run");
|
||||
assertThat(ReservedList.get("xn--q9jyb4c_common-reserved")).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,10 +31,14 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.batch.RelockDomainAction;
|
||||
import google.registry.model.billing.BillingBase;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
@@ -67,6 +71,21 @@ public final class DomainLockUtilsTest {
|
||||
private static final String DOMAIN_NAME = "example.tld";
|
||||
private static final String POC_ID = "marla.singer@example.com";
|
||||
|
||||
private static final User NON_ADMIN_USER =
|
||||
new User.Builder()
|
||||
.setEmailAddress("user@theregistrar.com")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(ImmutableMap.of("TheRegistrar", RegistrarRole.PRIMARY_CONTACT))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
private static final User ADMIN_USER =
|
||||
new User.Builder()
|
||||
.setEmailAddress("admin@theregistrar.com")
|
||||
.setUserRoles(new UserRoles.Builder().setIsAdmin(true).build())
|
||||
.build();
|
||||
|
||||
private final FakeClock clock = new FakeClock(Instant.now());
|
||||
private DomainLockUtils domainLockUtils;
|
||||
private CloudTasksHelper cloudTasksHelper = new CloudTasksHelper(clock);
|
||||
@@ -122,7 +141,7 @@ public final class DomainLockUtilsTest {
|
||||
clock.advanceBy(Duration.ofDays(1));
|
||||
RegistryLock lock =
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), false);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), NON_ADMIN_USER);
|
||||
verifyProperlyLockedDomain(false);
|
||||
}
|
||||
|
||||
@@ -135,7 +154,7 @@ public final class DomainLockUtilsTest {
|
||||
RegistryLock unlockRequest =
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.empty());
|
||||
domainLockUtils.verifyVerificationCode(unlockRequest.getVerificationCode(), false);
|
||||
domainLockUtils.verifyVerificationCode(unlockRequest.getVerificationCode(), NON_ADMIN_USER);
|
||||
assertThat(loadByEntity(domain).getStatusValues()).containsNoneIn(REGISTRY_LOCK_STATUSES);
|
||||
}
|
||||
|
||||
@@ -143,7 +162,7 @@ public final class DomainLockUtilsTest {
|
||||
void testSuccess_applyLockDomain() {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), false);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), NON_ADMIN_USER);
|
||||
verifyProperlyLockedDomain(false);
|
||||
}
|
||||
|
||||
@@ -153,7 +172,7 @@ public final class DomainLockUtilsTest {
|
||||
RegistryLock unlock =
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.empty());
|
||||
domainLockUtils.verifyVerificationCode(unlock.getVerificationCode(), false);
|
||||
domainLockUtils.verifyVerificationCode(unlock.getVerificationCode(), NON_ADMIN_USER);
|
||||
verifyProperlyUnlockedDomain(false);
|
||||
}
|
||||
|
||||
@@ -161,7 +180,7 @@ public final class DomainLockUtilsTest {
|
||||
void testSuccess_applyAdminLock_onlyHistoryEntry() {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), true);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), ADMIN_USER);
|
||||
verifyProperlyLockedDomain(true);
|
||||
}
|
||||
|
||||
@@ -169,11 +188,11 @@ public final class DomainLockUtilsTest {
|
||||
void testSuccess_applyAdminUnlock_onlyHistoryEntry() {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), true);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), ADMIN_USER);
|
||||
RegistryLock unlock =
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", true, Optional.empty());
|
||||
domainLockUtils.verifyVerificationCode(unlock.getVerificationCode(), true);
|
||||
domainLockUtils.verifyVerificationCode(unlock.getVerificationCode(), ADMIN_USER);
|
||||
verifyProperlyUnlockedDomain(true);
|
||||
}
|
||||
|
||||
@@ -194,7 +213,7 @@ public final class DomainLockUtilsTest {
|
||||
void testSuccess_administrativelyUnlock_nonAdmin() {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), false);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), NON_ADMIN_USER);
|
||||
domainLockUtils.administrativelyApplyUnlock(
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.empty());
|
||||
verifyProperlyUnlockedDomain(false);
|
||||
@@ -204,7 +223,7 @@ public final class DomainLockUtilsTest {
|
||||
void testSuccess_administrativelyUnlock_admin() {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), true);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), ADMIN_USER);
|
||||
domainLockUtils.administrativelyApplyUnlock(
|
||||
DOMAIN_NAME, "TheRegistrar", true, Optional.empty());
|
||||
verifyProperlyUnlockedDomain(true);
|
||||
@@ -218,7 +237,7 @@ public final class DomainLockUtilsTest {
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.empty());
|
||||
RegistryLock newLock =
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false);
|
||||
newLock = domainLockUtils.verifyVerificationCode(newLock.getVerificationCode(), false);
|
||||
newLock = domainLockUtils.verifyVerificationCode(newLock.getVerificationCode(), NON_ADMIN_USER);
|
||||
assertThat(
|
||||
getRegistryLockByRevisionId(oldLock.getRevisionId()).get().getRelock().getRevisionId())
|
||||
.isEqualTo(newLock.getRevisionId());
|
||||
@@ -252,7 +271,7 @@ public final class DomainLockUtilsTest {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.of(Duration.ofHours(6)));
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), false);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), NON_ADMIN_USER);
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new TaskMatcher()
|
||||
@@ -302,7 +321,7 @@ public final class DomainLockUtilsTest {
|
||||
void testFailure_createUnlock_alreadyPendingUnlock() {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), false);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), NON_ADMIN_USER);
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.empty());
|
||||
|
||||
@@ -321,7 +340,7 @@ public final class DomainLockUtilsTest {
|
||||
void testFailure_createUnlock_nonAdminUnlockingAdmin() {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), true);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), ADMIN_USER);
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
@@ -385,12 +404,13 @@ public final class DomainLockUtilsTest {
|
||||
void testFailure_applyLock_alreadyApplied() {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), false);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), NON_ADMIN_USER);
|
||||
domain = loadByEntity(domain);
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), false));
|
||||
() ->
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), NON_ADMIN_USER));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Lock/unlock with code 123456789ABCDEFGHJKLMNPQRSTUVWXY is already completed");
|
||||
@@ -405,7 +425,7 @@ public final class DomainLockUtilsTest {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), true));
|
||||
() -> domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), ADMIN_USER));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("The pending lock has expired; please try again");
|
||||
assertNoDomainChanges();
|
||||
}
|
||||
@@ -417,31 +437,146 @@ public final class DomainLockUtilsTest {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), false));
|
||||
() ->
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), NON_ADMIN_USER));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Non-admin user cannot complete admin lock");
|
||||
assertNoDomainChanges();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_applyLock_noPermission() {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false);
|
||||
User userWithoutPermission =
|
||||
new User.Builder()
|
||||
.setEmailAddress("unauthorized@example.com")
|
||||
.setUserRoles(new UserRoles.Builder().build())
|
||||
.build();
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainLockUtils.verifyVerificationCode(
|
||||
lock.getVerificationCode(), userWithoutPermission));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"User unauthorized@example.com does not have registry lock permission on registrar"
|
||||
+ " TheRegistrar");
|
||||
assertNoDomainChanges();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_applyUnlock_noPermission() {
|
||||
domainLockUtils.administrativelyApplyLock(DOMAIN_NAME, "TheRegistrar", POC_ID, false);
|
||||
RegistryLock unlock =
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.empty());
|
||||
User userWithoutPermission =
|
||||
new User.Builder()
|
||||
.setEmailAddress("unauthorized@example.com")
|
||||
.setUserRoles(new UserRoles.Builder().build())
|
||||
.build();
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainLockUtils.verifyVerificationCode(
|
||||
unlock.getVerificationCode(), userWithoutPermission));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"User unauthorized@example.com does not have registry lock permission on registrar"
|
||||
+ " TheRegistrar");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_applyUnlock_alreadyUnlocked() {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), false);
|
||||
domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), NON_ADMIN_USER);
|
||||
RegistryLock unlock =
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.empty());
|
||||
domainLockUtils.verifyVerificationCode(unlock.getVerificationCode(), false);
|
||||
domainLockUtils.verifyVerificationCode(unlock.getVerificationCode(), NON_ADMIN_USER);
|
||||
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.verifyVerificationCode(unlock.getVerificationCode(), false));
|
||||
() ->
|
||||
domainLockUtils.verifyVerificationCode(
|
||||
unlock.getVerificationCode(), NON_ADMIN_USER));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Lock/unlock with code Zabcdefghijkmnopqrstuvwxyz123456 is already completed");
|
||||
assertNoDomainChanges();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_applyUnlock_supersededByNewerLock() {
|
||||
domainLockUtils.administrativelyApplyLock(DOMAIN_NAME, "TheRegistrar", POC_ID, false);
|
||||
RegistryLock unlockRequest =
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, Optional.empty());
|
||||
clock.advanceOneMilli();
|
||||
domainLockUtils.administrativelyApplyLock(DOMAIN_NAME, "TheRegistrar", null, true);
|
||||
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainLockUtils.verifyVerificationCode(
|
||||
unlockRequest.getVerificationCode(), NON_ADMIN_USER));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"The pending unlock on example.tld has been superseded; please request a new unlock");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_applyUnlock_supersededByInterleavedUnlockAndLock() {
|
||||
// Lock the domain initially
|
||||
domainLockUtils.administrativelyApplyLock(DOMAIN_NAME, "TheRegistrar", null, true);
|
||||
|
||||
// 1. Unlock A requested
|
||||
clock.advanceOneMilli();
|
||||
RegistryLock unlockA =
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", true, Optional.empty());
|
||||
|
||||
// 2. Unlock B requested
|
||||
clock.advanceOneMilli();
|
||||
RegistryLock unlockB =
|
||||
domainLockUtils.saveNewRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", true, Optional.empty());
|
||||
|
||||
// 3. Unlock B completed
|
||||
clock.advanceOneMilli();
|
||||
domainLockUtils.verifyVerificationCode(unlockB.getVerificationCode(), ADMIN_USER);
|
||||
|
||||
// 4. Lock C requested
|
||||
clock.advanceOneMilli();
|
||||
RegistryLock lockC =
|
||||
domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, true);
|
||||
|
||||
// 5. Lock C applied
|
||||
clock.advanceOneMilli();
|
||||
domainLockUtils.verifyVerificationCode(lockC.getVerificationCode(), ADMIN_USER);
|
||||
|
||||
// 6. Unlock A completion attempt should fail since it has been superseded -- the second unlock
|
||||
// request rewrote the verification code
|
||||
clock.advanceOneMilli();
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainLockUtils.verifyVerificationCode(unlockA.getVerificationCode(), ADMIN_USER));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
String.format("Invalid verification code \"%s\"", unlockA.getVerificationCode()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_applyLock_alreadyLocked() {
|
||||
RegistryLock lock =
|
||||
@@ -453,7 +588,7 @@ public final class DomainLockUtilsTest {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domainLockUtils.verifyVerificationCode(verificationCode, false));
|
||||
() -> domainLockUtils.verifyVerificationCode(verificationCode, NON_ADMIN_USER));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Domain example.tld is already locked");
|
||||
|
||||
// Failure during the lock acquisition portion shouldn't affect the SQL object
|
||||
|
||||
@@ -27,6 +27,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
import google.registry.model.registrar.Registrar.Type;
|
||||
@@ -56,11 +58,17 @@ class UnlockDomainCommandTest extends CommandTestCase<UnlockDomainCommand> {
|
||||
command.printStream = System.out;
|
||||
}
|
||||
|
||||
private static final User ADMIN_USER =
|
||||
new User.Builder()
|
||||
.setEmailAddress("admin@theregistrar.com")
|
||||
.setUserRoles(new UserRoles.Builder().setIsAdmin(true).build())
|
||||
.build();
|
||||
|
||||
private Domain persistLockedDomain(String domainName, String registrarId) {
|
||||
Domain domain = persistResource(DatabaseHelper.newDomain(domainName));
|
||||
RegistryLock lock =
|
||||
command.domainLockUtils.saveNewRegistryLockRequest(domainName, registrarId, null, true);
|
||||
command.domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), true);
|
||||
command.domainLockUtils.verifyVerificationCode(lock.getVerificationCode(), ADMIN_USER);
|
||||
return reloadResource(domain);
|
||||
}
|
||||
|
||||
|
||||
@@ -209,9 +209,7 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
||||
"--name=" + TLD_TEST,
|
||||
"--input=" + Paths.get(tmpFile.getPath())));
|
||||
assertThat(thrown.getMessage())
|
||||
.isEqualTo(
|
||||
"The --build_environment flag must be used when running update_premium_list in"
|
||||
+ " production");
|
||||
.isEqualTo("The --build_environment flag must be used when running in production");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -139,9 +139,7 @@ class UpdateReservedListCommandTest
|
||||
"--name=xn--q9jyb4c_common-reserved",
|
||||
"--input=" + reservedTermsPath));
|
||||
assertThat(thrown.getMessage())
|
||||
.isEqualTo(
|
||||
"The --build_environment flag must be used when running update_reserved_list in"
|
||||
+ " production");
|
||||
.isEqualTo("The --build_environment flag must be used when running in production");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2026 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link CspFilter}. */
|
||||
class CspFilterTest {
|
||||
|
||||
@Test
|
||||
void testDoFilter_setsHeaders() throws Exception {
|
||||
CspFilter filter = new CspFilter();
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
FilterChain chain = mock(FilterChain.class);
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
verify(response)
|
||||
.setHeader(
|
||||
"Content-Security-Policy", "default-src 'self'; object-src 'none'; base-uri 'self';");
|
||||
verify(response).setHeader("X-Content-Type-Options", "nosniff");
|
||||
verify(response).setHeader("X-Frame-Options", "DENY");
|
||||
verify(chain).doFilter(request, response);
|
||||
}
|
||||
}
|
||||
@@ -21,10 +21,13 @@ import com.google.gson.Gson;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.security.JsonHttpTestUtils;
|
||||
import google.registry.testing.ConsoleApiParamsUtils;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.tools.GsonUtils;
|
||||
import jakarta.servlet.ServletInputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
@@ -51,4 +54,8 @@ public abstract class ConsoleActionBaseTestCase {
|
||||
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
|
||||
response = (FakeResponse) consoleApiParams.response();
|
||||
}
|
||||
|
||||
protected static ServletInputStream createServletInputStream(String data) {
|
||||
return JsonHttpTestUtils.createServletInputStream(data.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
+5
-11
@@ -23,7 +23,6 @@ import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -47,9 +46,7 @@ import google.registry.ui.server.console.ConsoleEppPasswordAction.EppPasswordDat
|
||||
import google.registry.util.EmailMessage;
|
||||
import jakarta.mail.internet.AddressException;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -167,16 +164,13 @@ class ConsoleEppPasswordActionTest extends ConsoleActionBaseTestCase {
|
||||
AuthenticatedRegistrarAccessor.createForTesting(
|
||||
ImmutableSetMultimap.of("TheRegistrar", OWNER));
|
||||
when(consoleApiParams.request().getMethod()).thenReturn(Action.Method.POST.toString());
|
||||
doReturn(
|
||||
new BufferedReader(
|
||||
new StringReader(
|
||||
String.format(
|
||||
eppPostData, registrarId, oldPassword, newPassword, newPasswordRepeat))))
|
||||
.when(consoleApiParams.request())
|
||||
.getReader();
|
||||
Optional<EppPasswordData> maybePasswordChangeRequest =
|
||||
ConsoleModule.provideEppPasswordChangeRequest(
|
||||
GSON, RequestModule.provideJsonBody(consoleApiParams.request(), GSON));
|
||||
GSON,
|
||||
RequestModule.provideJsonBody(
|
||||
String.format(
|
||||
eppPostData, registrarId, oldPassword, newPassword, newPasswordRepeat),
|
||||
GSON));
|
||||
|
||||
return new ConsoleEppPasswordAction(
|
||||
consoleApiParams, authenticatedRegistrarAccessor, maybePasswordChangeRequest);
|
||||
|
||||
+15
-1
@@ -182,6 +182,20 @@ public class ConsoleRegistryLockVerifyActionTest extends ConsoleActionBaseTestCa
|
||||
.containsAtLeastElementsIn(REGISTRY_LOCK_STATUSES);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_noPermission() {
|
||||
saveRegistryLock(createDefaultLockBuilder().build());
|
||||
user = user.asBuilder().setUserRoles(new UserRoles.Builder().build()).build();
|
||||
action = createAction(DEFAULT_CODE);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo(
|
||||
"User user@theregistrar.com does not have registry lock permission on registrar"
|
||||
+ " TheRegistrar");
|
||||
assertThat(loadByEntity(defaultDomain).getStatusValues()).containsExactly(StatusValue.INACTIVE);
|
||||
}
|
||||
|
||||
private RegistryLock.Builder createDefaultLockBuilder() {
|
||||
return new RegistryLock.Builder()
|
||||
.setRepoId(defaultDomain.getRepoId())
|
||||
@@ -194,7 +208,7 @@ public class ConsoleRegistryLockVerifyActionTest extends ConsoleActionBaseTestCa
|
||||
private ConsoleRegistryLockVerifyAction createAction(String verificationCode) {
|
||||
AuthResult authResult = AuthResult.createUser(user);
|
||||
ConsoleApiParams params = ConsoleApiParamsUtils.createFake(authResult);
|
||||
when(params.request().getMethod()).thenReturn("GET");
|
||||
when(params.request().getMethod()).thenReturn("POST");
|
||||
when(params.request().getServerName()).thenReturn("registrarconsole.tld");
|
||||
DomainLockUtils domainLockUtils =
|
||||
new DomainLockUtils(
|
||||
|
||||
+1
-8
@@ -21,7 +21,6 @@ import static google.registry.testing.DatabaseHelper.loadSingleton;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -43,9 +42,7 @@ import google.registry.util.EmailMessage;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import jakarta.mail.internet.AddressException;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -219,12 +216,8 @@ class ConsoleUpdateRegistrarActionTest extends ConsoleActionBaseTestCase {
|
||||
|
||||
private ConsoleUpdateRegistrarAction createAction(String requestData) throws IOException {
|
||||
when(consoleApiParams.request().getMethod()).thenReturn(Action.Method.POST.toString());
|
||||
doReturn(new BufferedReader(new StringReader(requestData)))
|
||||
.when(consoleApiParams.request())
|
||||
.getReader();
|
||||
Optional<Registrar> maybeRegistrarUpdateData =
|
||||
ConsoleModule.provideRegistrar(
|
||||
GSON, RequestModule.provideJsonBody(consoleApiParams.request(), GSON));
|
||||
ConsoleModule.provideRegistrar(GSON, RequestModule.provideJsonBody(requestData, GSON));
|
||||
return new ConsoleUpdateRegistrarAction(consoleApiParams, maybeRegistrarUpdateData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,10 @@ import static jakarta.servlet.http.HttpServletResponse.SC_CREATED;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.api.services.directory.Directory;
|
||||
@@ -243,7 +246,8 @@ class ConsoleUsersActionTest extends ConsoleActionBaseTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_deletesUser() throws IOException {
|
||||
void testSuccess_deletesUser_nonConsoleMintedAddress_skipsWorkspaceAccountDeletion()
|
||||
throws IOException {
|
||||
User user1 = DatabaseHelper.loadByKey(VKey.create(User.class, "test1@test.com"));
|
||||
AuthResult authResult =
|
||||
AuthResult.createUser(
|
||||
@@ -265,6 +269,44 @@ class ConsoleUsersActionTest extends ConsoleActionBaseTestCase {
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(DatabaseHelper.loadByKeyIfPresent(VKey.create(User.class, "test2@test.com")))
|
||||
.isEmpty();
|
||||
verify(users, never()).delete(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_deletesUser_consoleMintedAddress_deletesWorkspaceAccount() throws IOException {
|
||||
User user1 = DatabaseHelper.loadByKey(VKey.create(User.class, "test1@test.com"));
|
||||
AuthResult authResult =
|
||||
AuthResult.createUser(
|
||||
user1
|
||||
.asBuilder()
|
||||
.setUserRoles(user1.getUserRoles().asBuilder().setIsAdmin(true).build())
|
||||
.build());
|
||||
String mintedEmail = "abc.TheRegistrar@email.com";
|
||||
DatabaseHelper.persistResource(
|
||||
new User.Builder()
|
||||
.setEmailAddress(mintedEmail)
|
||||
.setUserRoles(
|
||||
new UserRoles()
|
||||
.asBuilder()
|
||||
.setRegistrarRoles(
|
||||
ImmutableMap.of("TheRegistrar", RegistrarRole.PRIMARY_CONTACT))
|
||||
.build())
|
||||
.build());
|
||||
|
||||
ConsoleUsersAction action =
|
||||
createAction(
|
||||
Optional.of(ConsoleApiParamsUtils.createFake(authResult)),
|
||||
Optional.of("DELETE"),
|
||||
Optional.of(
|
||||
new UserData(mintedEmail, null, RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
action.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
when(directory.users()).thenReturn(users);
|
||||
when(users.delete(mintedEmail)).thenReturn(delete);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(DatabaseHelper.loadByKeyIfPresent(VKey.create(User.class, mintedEmail))).isEmpty();
|
||||
verify(users).delete(mintedEmail);
|
||||
verify(delete).execute();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -23,7 +23,6 @@ import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.SqlHelper.saveRegistrar;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -42,9 +41,6 @@ import google.registry.testing.ConsoleApiParamsUtils;
|
||||
import google.registry.testing.DeterministicStringGenerator;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.util.StringGenerator;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -222,20 +218,9 @@ class RegistrarsActionTest extends ConsoleActionBaseTestCase {
|
||||
return new RegistrarsAction(
|
||||
consoleApiParams, Optional.ofNullable(null), passwordGenerator, passcodeGenerator);
|
||||
} else {
|
||||
try {
|
||||
doReturn(new BufferedReader(new StringReader(registrarParamMap.toString())))
|
||||
.when(consoleApiParams.request())
|
||||
.getReader();
|
||||
} catch (IOException e) {
|
||||
return new RegistrarsAction(
|
||||
consoleApiParams,
|
||||
Optional.ofNullable(null),
|
||||
passwordGenerator,
|
||||
passcodeGenerator);
|
||||
}
|
||||
Optional<Registrar> maybeRegistrar =
|
||||
ConsoleModule.provideRegistrar(
|
||||
GSON, RequestModule.provideJsonBody(consoleApiParams.request(), GSON));
|
||||
GSON, RequestModule.provideJsonBody(registrarParamMap.toString(), GSON));
|
||||
return new RegistrarsAction(
|
||||
consoleApiParams, maybeRegistrar, passwordGenerator, passcodeGenerator);
|
||||
}
|
||||
|
||||
+1
-7
@@ -19,7 +19,6 @@ import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableO
|
||||
import static google.registry.testing.DatabaseHelper.loadSingleton;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -41,9 +40,7 @@ import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.ui.server.console.ConsoleActionBaseTestCase;
|
||||
import google.registry.ui.server.console.ConsoleModule;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.HashMap;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -143,13 +140,10 @@ public class RdapRegistrarFieldsActionTest extends ConsoleActionBaseTestCase {
|
||||
consoleApiParams = ConsoleApiParamsUtils.createFake(AuthResult.createUser(user));
|
||||
response = (FakeResponse) consoleApiParams.response();
|
||||
when(consoleApiParams.request().getMethod()).thenReturn(Action.Method.POST.toString());
|
||||
doReturn(new BufferedReader(new StringReader(uiRegistrarMap.toString())))
|
||||
.when(consoleApiParams.request())
|
||||
.getReader();
|
||||
return new RdapRegistrarFieldsAction(
|
||||
consoleApiParams,
|
||||
registrarAccessor,
|
||||
ConsoleModule.provideRegistrar(
|
||||
GSON, RequestModule.provideJsonBody(consoleApiParams.request(), GSON)));
|
||||
GSON, RequestModule.provideJsonBody(uiRegistrarMap.toString(), GSON)));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-8
@@ -22,7 +22,6 @@ import static google.registry.testing.SqlHelper.saveRegistrar;
|
||||
import static google.registry.util.DateTimeUtils.START_INSTANT;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -37,9 +36,7 @@ import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.ui.server.console.ConsoleActionBaseTestCase;
|
||||
import google.registry.ui.server.console.ConsoleModule;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -170,12 +167,8 @@ class SecurityActionTest extends ConsoleActionBaseTestCase {
|
||||
String registrarId, String jsonBody, CertificateChecker certificateChecker)
|
||||
throws IOException {
|
||||
when(consoleApiParams.request().getMethod()).thenReturn(Action.Method.POST.toString());
|
||||
doReturn(new BufferedReader(new StringReader(jsonBody)))
|
||||
.when(consoleApiParams.request())
|
||||
.getReader();
|
||||
Optional<Registrar> maybeRegistrar =
|
||||
ConsoleModule.provideRegistrar(
|
||||
GSON, RequestModule.provideJsonBody(consoleApiParams.request(), GSON));
|
||||
ConsoleModule.provideRegistrar(GSON, RequestModule.provideJsonBody(jsonBody, GSON));
|
||||
return new SecurityAction(
|
||||
consoleApiParams, certificateChecker, registrarAccessor, registrarId, maybeRegistrar);
|
||||
}
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<response>
|
||||
<result code="1000">
|
||||
<msg>Command completed successfully</msg>
|
||||
</result>
|
||||
<resData>
|
||||
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:cd>
|
||||
<domain:name avail="true">example1.tld</domain:name>
|
||||
</domain:cd>
|
||||
<domain:cd>
|
||||
<domain:name avail="true">example2.tld</domain:name>
|
||||
</domain:cd>
|
||||
<domain:cd>
|
||||
<domain:name avail="true">example3.tld</domain:name>
|
||||
</domain:cd>
|
||||
</domain:chkData>
|
||||
</resData>
|
||||
<extension>
|
||||
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:cd>
|
||||
<fee:objID>example1.tld</fee:objID>
|
||||
<fee:class>standard</fee:class>
|
||||
<fee:command name="create">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="Expiry Access Period, fee expires: 2009-01-06T01:00:00Z">110.00</fee:fee>
|
||||
</fee:command>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
<fee:objID>example2.tld</fee:objID>
|
||||
<fee:class>standard</fee:class>
|
||||
<fee:command name="create">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
</fee:command>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
<fee:objID>example3.tld</fee:objID>
|
||||
<fee:class>standard</fee:class>
|
||||
<fee:command name="create">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
</fee:command>
|
||||
</fee:cd>
|
||||
</fee:chkData>
|
||||
</extension>
|
||||
<trID>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
<svTRID>server-trid</svTRID>
|
||||
</trID>
|
||||
</response>
|
||||
</epp>
|
||||
@@ -0,0 +1,26 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>rich.example</domain:name>
|
||||
<domain:period unit="y">2</domain:period>
|
||||
<domain:ns>
|
||||
<domain:hostObj>ns1.example.net</domain:hostObj>
|
||||
<domain:hostObj>ns2.example.net</domain:hostObj>
|
||||
</domain:ns>
|
||||
<domain:authInfo>
|
||||
<domain:pw>2fooBAR</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:create>
|
||||
</create>
|
||||
<extension>
|
||||
<fee:create xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:fee description="create">200.00</fee:fee>
|
||||
<fee:fee description="Expiry Access Period">100.00</fee:fee>
|
||||
</fee:create>
|
||||
</extension>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<response>
|
||||
<result code="1000">
|
||||
<msg>Command completed successfully</msg>
|
||||
</result>
|
||||
<resData>
|
||||
<domain:creData
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>rich.example</domain:name>
|
||||
<domain:crDate>1999-04-03T22:00:00.0Z</domain:crDate>
|
||||
<domain:exDate>2001-04-03T22:00:00.0Z</domain:exDate>
|
||||
</domain:creData>
|
||||
</resData>
|
||||
<extension>
|
||||
<fee:creData xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:fee description="create">200.00</fee:fee>
|
||||
<fee:fee description="Expiry Access Period, fee expires: 1999-04-03T23:00:00Z">100.00</fee:fee>
|
||||
</fee:creData>
|
||||
</extension>
|
||||
<trID>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
<svTRID>server-trid</svTRID>
|
||||
</trID>
|
||||
</response>
|
||||
</epp>
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<response>
|
||||
<result code="1000">
|
||||
<msg>Command completed successfully</msg>
|
||||
</result>
|
||||
<resData>
|
||||
<domain:creData
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:crDate>1999-04-03T22:00:00.0Z</domain:crDate>
|
||||
<domain:exDate>2001-04-03T22:00:00.0Z</domain:exDate>
|
||||
</domain:creData>
|
||||
</resData>
|
||||
<extension>
|
||||
<fee:creData xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:fee description="create">24.00</fee:fee>
|
||||
<fee:fee description="Expiry Access Period, fee expires: 1999-04-03T23:00:00Z">100.00</fee:fee>
|
||||
</fee:creData>
|
||||
</extension>
|
||||
<trID>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
<svTRID>server-trid</svTRID>
|
||||
</trID>
|
||||
</response>
|
||||
</epp>
|
||||
@@ -32,6 +32,8 @@ eapFeeSchedule:
|
||||
currency: "USD"
|
||||
amount: 0.00
|
||||
escrowEnabled: false
|
||||
expiryAccessPeriodTransitions:
|
||||
"1970-01-01T00:00:00.000Z": "DISABLED"
|
||||
idnTables:
|
||||
- "EXTENDED_LATIN"
|
||||
- "JA"
|
||||
|
||||
@@ -82,7 +82,7 @@ CONSOLE /console-api/password-reset-verify PasswordResetVerifyA
|
||||
CONSOLE /console-api/registrar ConsoleUpdateRegistrarAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/registrars RegistrarsAction GET,POST n USER PUBLIC
|
||||
CONSOLE /console-api/registry-lock ConsoleRegistryLockAction GET,POST n USER PUBLIC
|
||||
CONSOLE /console-api/registry-lock-verify ConsoleRegistryLockVerifyAction GET n USER PUBLIC
|
||||
CONSOLE /console-api/registry-lock-verify ConsoleRegistryLockVerifyAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/settings/contacts ContactAction GET,POST,DELETE,PUT n USER PUBLIC
|
||||
CONSOLE /console-api/settings/rdap-fields RdapRegistrarFieldsAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/settings/security SecurityAction POST n USER PUBLIC
|
||||
|
||||
@@ -21,15 +21,12 @@
|
||||
-- "tld":"","tlds":["a.how"],"icannActivityReportField":"srs-dom-check"}
|
||||
|
||||
SELECT
|
||||
-- Remove quotation marks from tld fields.
|
||||
REGEXP_EXTRACT(tld, '^"(.*)"$') AS tld,
|
||||
tld,
|
||||
activityReportField AS metricName,
|
||||
COUNT(*) AS count
|
||||
FROM (
|
||||
SELECT
|
||||
-- TODO(b/32486667): Replace with JSON.parse() UDF when available for views
|
||||
SPLIT(
|
||||
REGEXP_EXTRACT(JSON_EXTRACT(json, '$.tlds'), r'^\[(.*)\]$')) AS tlds,
|
||||
JSON_EXTRACT_STRING_ARRAY(json, '$.tlds') AS tlds,
|
||||
JSON_EXTRACT_SCALAR(json,
|
||||
'$.resourceType') AS resourceType,
|
||||
JSON_EXTRACT_SCALAR(json,
|
||||
@@ -43,10 +40,10 @@ FROM (
|
||||
WHERE
|
||||
STARTS_WITH(jsonPayload.message, "FLOW-LOG-SIGNATURE-METADATA")
|
||||
AND _TABLE_SUFFIX BETWEEN '20170901' AND '20170930')
|
||||
) AS regexes
|
||||
) AS json_parsed
|
||||
JOIN
|
||||
-- Unnest the JSON-parsed tlds.
|
||||
UNNEST(regexes.tlds) AS tld
|
||||
UNNEST(json_parsed.tlds) AS tld
|
||||
-- Exclude cases that can't be tabulated correctly, where activityReportField
|
||||
-- is null/empty, or TLD is null/empty despite being a domain flow.
|
||||
WHERE
|
||||
|
||||
@@ -19,6 +19,8 @@ eapFeeSchedule:
|
||||
currency: "USD"
|
||||
amount: 0.00
|
||||
escrowEnabled: false
|
||||
expiryAccessPeriodTransitions:
|
||||
"1970-01-01T00:00:00.000Z": "DISABLED"
|
||||
idnTables: []
|
||||
invoicingEnabled: false
|
||||
lordnUsername: null
|
||||
|
||||
@@ -23,6 +23,8 @@ eapFeeSchedule:
|
||||
currency: "USD"
|
||||
amount: 0.00
|
||||
escrowEnabled: false
|
||||
expiryAccessPeriodTransitions:
|
||||
"1970-01-01T00:00:00.000Z": "DISABLED"
|
||||
idnTables:
|
||||
- "foo"
|
||||
invoicingEnabled: false
|
||||
|
||||
@@ -19,6 +19,8 @@ eapFeeSchedule:
|
||||
currency: "USD"
|
||||
amount: 0.00
|
||||
escrowEnabled: false
|
||||
expiryAccessPeriodTransitions:
|
||||
"1970-01-01T00:00:00.000Z": "DISABLED"
|
||||
idnTables: []
|
||||
invoicingEnabled: false
|
||||
lordnUsername: null
|
||||
|
||||
@@ -23,6 +23,8 @@ eapFeeSchedule:
|
||||
currency: "USD"
|
||||
amount: 0.00
|
||||
escrowEnabled: false
|
||||
expiryAccessPeriodTransitions:
|
||||
"1970-01-01T00:00:00.000Z": "DISABLED"
|
||||
idnTables: []
|
||||
invoicingEnabled: false
|
||||
lordnUsername: null
|
||||
|
||||
@@ -23,6 +23,8 @@ eapFeeSchedule:
|
||||
currency: "USD"
|
||||
amount: 0.00
|
||||
escrowEnabled: false
|
||||
expiryAccessPeriodTransitions:
|
||||
"1970-01-01T00:00:00.000Z": "DISABLED"
|
||||
idnTables: []
|
||||
invoicingEnabled: false
|
||||
lordnUsername: null
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user