mirror of
https://github.com/google/nomulus
synced 2026-05-21 23:31:51 +00:00
Compare commits
37 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1eef260da9 | ||
|
|
9d0ff74377 | ||
|
|
7a301edab7 | ||
|
|
08bcf579a5 | ||
|
|
7d2330c943 | ||
|
|
670941bec8 | ||
|
|
1f516e34b6 | ||
|
|
70942c87d1 | ||
|
|
406059db72 | ||
|
|
abc1a0ef3d | ||
|
|
7b47ecb1f1 | ||
|
|
469d62703a | ||
|
|
009fda67b7 | ||
|
|
e492936cec | ||
|
|
d1d59c1afd | ||
|
|
7b786eaf1f | ||
|
|
45c5d12743 | ||
|
|
73ab95bd9d | ||
|
|
f85cf57e36 | ||
|
|
5e36cf30c3 | ||
|
|
829be0777b | ||
|
|
c0ac9bdba4 | ||
|
|
58ec0f826d | ||
|
|
f9e0908022 | ||
|
|
b21e1a1935 | ||
|
|
0112b3ae06 | ||
|
|
a4903c27b9 | ||
|
|
2166c28d6d | ||
|
|
891e7c0174 | ||
|
|
64f5971275 | ||
|
|
818944317f | ||
|
|
ea96ed300f | ||
|
|
8415c8bbe4 | ||
|
|
dc48c257b5 | ||
|
|
2bf3867532 | ||
|
|
44f44be643 | ||
|
|
f61579b350 |
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -117,3 +117,6 @@ core/**/registrar_dbg*.css
|
||||
# Appengine generated files
|
||||
core/WEB-INF/appengine-generated/*.bin
|
||||
core/WEB-INF/appengine-generated/*.xml
|
||||
|
||||
# jEnv
|
||||
.java-version
|
||||
|
||||
1
.java-version
Normal file
1
.java-version
Normal file
@@ -0,0 +1 @@
|
||||
17
|
||||
54
build.gradle
54
build.gradle
@@ -211,6 +211,30 @@ allprojects {
|
||||
options.fork = true
|
||||
options.forkOptions.executable =
|
||||
file("${System.env.JAVA_HOME}/bin/javac")
|
||||
options.compilerArgs = ["--add-exports",
|
||||
"jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
|
||||
"--add-exports",
|
||||
"jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
|
||||
"--add-exports",
|
||||
"jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
|
||||
"--add-exports",
|
||||
"jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
|
||||
"--add-exports",
|
||||
"jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
|
||||
"--add-exports",
|
||||
"jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
|
||||
"--add-exports",
|
||||
"jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
|
||||
"--add-exports",
|
||||
"jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"]
|
||||
options.forkOptions.jvmArgs = ["-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
|
||||
"-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
|
||||
"-J--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
|
||||
"-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
|
||||
"-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
|
||||
"-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
|
||||
"-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
|
||||
"-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -358,16 +382,12 @@ subprojects {
|
||||
|
||||
apply from: "${rootDir.path}/java_common.gradle"
|
||||
|
||||
if (!rootProject.enableCrossReferencing.toBoolean()) {
|
||||
compileJava {
|
||||
// TODO: Remove this once we migrate off of AppEngine.
|
||||
options.release = 8
|
||||
}
|
||||
compileTestJava {
|
||||
// TODO: Remove this once we migrate off of AppEngine.
|
||||
options.release = 8
|
||||
}
|
||||
}
|
||||
// When changing Java version here, be sure to update BEAM Java runtime:
|
||||
// search for `flex-template-base-image` and update the parameter value.
|
||||
// There are at least two instances, one in core/build.gradle, one in
|
||||
// release/stage_beam_pipeline.sh
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
|
||||
project.tasks.test.dependsOn runPresubmits
|
||||
|
||||
@@ -479,10 +499,14 @@ def createGetBuildSrcDirectDepsTask(outputFileName) {
|
||||
|
||||
rootProject.ext {
|
||||
invokeJavaDiffFormatScript = { action ->
|
||||
println("JAVA_HOME=${System.env.JAVA_HOME}")
|
||||
println("PATH=${System.env.PATH}")
|
||||
println(ext.execInBash("type java", "${rootDir}"))
|
||||
println(ext.execInBash("java -version", "${rootDir}"))
|
||||
def javaHome = project.findProperty('org.gradle.java.home')
|
||||
def javaBin
|
||||
if (javaHome != null) {
|
||||
javaBin = "$javaHome/bin/java"
|
||||
} else {
|
||||
javaBin = ext.execInBash("which java", rootDir)
|
||||
}
|
||||
println("Running the formatting tool with $javaBin")
|
||||
def scriptDir = rootDir.path.endsWith('buildSrc')
|
||||
? "${rootDir}/../java-format"
|
||||
: "${rootDir}/java-format"
|
||||
@@ -493,7 +517,7 @@ rootProject.ext {
|
||||
def pythonExe = getPythonExecutable()
|
||||
|
||||
return ext.execInBash(
|
||||
"PYTHON=${pythonExe} ${formatDiffScript} ${action}", "${workingDir}")
|
||||
"JAVA=${javaBin} PYTHON=${pythonExe} ${formatDiffScript} ${action}", "${workingDir}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ PRESUBMITS = {
|
||||
PresubmitCheck(
|
||||
r".*Copyright 20\d{2} The Nomulus Authors\. All Rights Reserved\.",
|
||||
("java", "js", "soy", "sql", "py", "sh", "gradle", "ts"), {
|
||||
".git", "/build/", "/generated/", "/generated_tests/",
|
||||
".git", "/build/", "/bin/generated-sources/", "/bin/generated-test-sources/",
|
||||
"node_modules/", "LoggerConfig.java", "registrar_bin.",
|
||||
"registrar_dbg.", "google-java-format-diff.py",
|
||||
"nomulus.golden.sql", "soyutils_usegoog.js", "javascript/checks.js"
|
||||
|
||||
17478
console-webapp/package-lock.json
generated
17478
console-webapp/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { AfterViewInit, Component, ViewChild } from '@angular/core';
|
||||
import { AfterViewInit, Component, ViewChild, effect } from '@angular/core';
|
||||
import { RegistrarService } from './registrar/registrar.service';
|
||||
import { UserDataService } from './shared/services/userData.service';
|
||||
import { GlobalLoaderService } from './shared/services/globalLoader.service';
|
||||
@@ -36,11 +36,13 @@ export class AppComponent implements AfterViewInit {
|
||||
protected globalLoader: GlobalLoaderService,
|
||||
protected router: Router
|
||||
) {
|
||||
registrarService.activeRegistrarIdChange.subscribe(() => {
|
||||
this.renderRouter = false;
|
||||
setTimeout(() => {
|
||||
this.renderRouter = true;
|
||||
}, 400);
|
||||
effect(() => {
|
||||
if (registrarService.registrarId()) {
|
||||
this.renderRouter = false;
|
||||
setTimeout(() => {
|
||||
this.renderRouter = true;
|
||||
}, 400);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ export class DomainListService {
|
||||
) {
|
||||
return this.backendService
|
||||
.getDomains(
|
||||
this.registrarService.activeRegistrarId,
|
||||
this.registrarService.registrarId(),
|
||||
this.checkpointTime,
|
||||
pageNumber,
|
||||
resultsPerPage,
|
||||
|
||||
@@ -23,8 +23,10 @@ export class BillingWidgetComponent {
|
||||
constructor(public registrarService: RegistrarService) {}
|
||||
|
||||
public get driveFolderUrl(): string {
|
||||
if (this.registrarService?.registrar.driveFolderId) {
|
||||
return `https://drive.google.com/drive/folders/${this.registrarService?.registrar.driveFolderId}`;
|
||||
if (this.registrarService.registrar()?.driveFolderId) {
|
||||
return `https://drive.google.com/drive/folders/${
|
||||
this.registrarService.registrar()?.driveFolderId
|
||||
}`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -12,9 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, effect } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { RegistrarService } from './registrar.service';
|
||||
|
||||
@@ -24,22 +23,15 @@ import { RegistrarService } from './registrar.service';
|
||||
styleUrls: ['./emptyRegistrar.component.scss'],
|
||||
})
|
||||
export class EmptyRegistrar {
|
||||
private registrarIdChangeSubscription?: Subscription;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
protected registrarService: RegistrarService,
|
||||
private router: Router
|
||||
) {
|
||||
this.registrarIdChangeSubscription =
|
||||
registrarService.activeRegistrarIdChange.subscribe((newRegistrarId) => {
|
||||
if (newRegistrarId) {
|
||||
this.router.navigate([this.route.snapshot.paramMap.get('nextUrl')]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.registrarIdChangeSubscription?.unsubscribe();
|
||||
effect(() => {
|
||||
if (registrarService.registrarId()) {
|
||||
this.router.navigate([this.route.snapshot.paramMap.get('nextUrl')]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export class RegistrarGuard {
|
||||
_: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot
|
||||
): Promise<boolean> | boolean {
|
||||
if (this.registrarService.activeRegistrarId) {
|
||||
if (this.registrarService.registrarId()) {
|
||||
return true;
|
||||
}
|
||||
return this.router.navigate([
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, Subject, tap } from 'rxjs';
|
||||
import { Injectable, computed, signal } from '@angular/core';
|
||||
import { Observable, tap } from 'rxjs';
|
||||
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
import {
|
||||
@@ -52,9 +52,11 @@ export interface Registrar {
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class RegistrarService implements GlobalLoader {
|
||||
activeRegistrarId: string = '';
|
||||
registrars: Registrar[] = [];
|
||||
activeRegistrarIdChange: Subject<string> = new Subject<string>();
|
||||
registrarId = signal<string>('');
|
||||
registrars = signal<Registrar[]>([]);
|
||||
registrar = computed<Registrar | undefined>(() =>
|
||||
this.registrars().find((r) => r.registrarId === this.registrarId())
|
||||
);
|
||||
|
||||
constructor(
|
||||
private backend: BackendService,
|
||||
@@ -67,22 +69,15 @@ export class RegistrarService implements GlobalLoader {
|
||||
this.globalLoader.startGlobalLoader(this);
|
||||
}
|
||||
|
||||
public get registrar(): Registrar {
|
||||
return this.registrars.filter(
|
||||
(r) => r.registrarId === this.activeRegistrarId
|
||||
)[0];
|
||||
}
|
||||
|
||||
public updateSelectedRegistrar(registrarId: string) {
|
||||
this.activeRegistrarId = registrarId;
|
||||
this.activeRegistrarIdChange.next(registrarId);
|
||||
this.registrarId.set(registrarId);
|
||||
}
|
||||
|
||||
public loadRegistrars(): Observable<Registrar[]> {
|
||||
return this.backend.getRegistrars().pipe(
|
||||
tap((registrars) => {
|
||||
if (registrars) {
|
||||
this.registrars = registrars;
|
||||
this.registrars.set(registrars);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
routerLinkActive="active"
|
||||
*ngIf="isMobile; else desktop"
|
||||
>
|
||||
{{ registrarService.activeRegistrarId || "Select registrar" }}
|
||||
{{ registrarService.registrarId() || "Select registrar" }}
|
||||
<mat-icon>open_in_new</mat-icon>
|
||||
</button>
|
||||
<ng-template #desktop>
|
||||
<mat-form-field class="mat-form-field-density-5" appearance="fill">
|
||||
<mat-label>Registrar</mat-label>
|
||||
<mat-select
|
||||
[ngModel]="registrarService.activeRegistrarId"
|
||||
[ngModel]="registrarService.registrarId()"
|
||||
(selectionChange)="
|
||||
registrarService.updateSelectedRegistrar($event.value)
|
||||
"
|
||||
>
|
||||
<mat-option
|
||||
*ngFor="let registrar of registrarService.registrars"
|
||||
*ngFor="let registrar of registrarService.registrars()"
|
||||
[value]="registrar.registrarId"
|
||||
>
|
||||
{{ registrar.registrarId }}
|
||||
|
||||
@@ -87,7 +87,7 @@ export class RegistrarComponent {
|
||||
|
||||
constructor(protected registrarService: RegistrarService) {
|
||||
this.dataSource = new MatTableDataSource<Registrar>(
|
||||
registrarService.registrars
|
||||
registrarService.registrars()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
<div *ngIf="loading" class="contact__loading">
|
||||
@if (loading) {
|
||||
<div class="contact__loading">
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
</div>
|
||||
<div *ngIf="!loading">
|
||||
<div
|
||||
class="contact__empty-contacts"
|
||||
*ngIf="contactService.contacts.length === 0"
|
||||
>
|
||||
} @else {
|
||||
<div>
|
||||
@if (contactService.contacts().length === 0) {
|
||||
<div class="contact__empty-contacts">
|
||||
<mat-icon class="contact__empty-contacts-icon secondary-text"
|
||||
>apps_outage</mat-icon
|
||||
>
|
||||
<h1>No contacts found</h1>
|
||||
</div>
|
||||
<div *ngFor="let group of groupedData">
|
||||
<div class="contact__cards-wrapper" *ngIf="group.contacts.length">
|
||||
<h3>{{ group.label }}s</h3>
|
||||
<mat-divider></mat-divider>
|
||||
<div class="contact__cards">
|
||||
<mat-card class="contact__card" *ngFor="let contact of group.contacts">
|
||||
<mat-card-title>{{ contact.name }}</mat-card-title>
|
||||
<p *ngIf="contact.phoneNumber">{{ contact.phoneNumber }}</p>
|
||||
<p *ngIf="contact.emailAddress">{{ contact.emailAddress }}</p>
|
||||
<mat-card-actions class="contact__card-actions">
|
||||
<button
|
||||
mat-button
|
||||
color="primary"
|
||||
(click)="openDetails($event, contact)"
|
||||
>
|
||||
<mat-icon>edit</mat-icon>Edit
|
||||
</button>
|
||||
<button mat-button color="accent" (click)="deleteContact(contact)">
|
||||
<mat-icon>delete</mat-icon>Delete
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</div>
|
||||
} @else { @for (group of groupedContacts(); track group.emailAddress) {
|
||||
<div class="contact__cards-wrapper">
|
||||
<h3>{{ group.label }}s</h3>
|
||||
<mat-divider></mat-divider>
|
||||
<div class="contact__cards">
|
||||
<mat-card class="contact__card" *ngFor="let contact of group.contacts">
|
||||
<mat-card-title>{{ contact.name }}</mat-card-title>
|
||||
<p *ngIf="contact.phoneNumber">{{ contact.phoneNumber }}</p>
|
||||
<p *ngIf="contact.emailAddress">{{ contact.emailAddress }}</p>
|
||||
<mat-card-actions class="contact__card-actions">
|
||||
<button
|
||||
mat-button
|
||||
color="primary"
|
||||
(click)="openDetails($event, contact)"
|
||||
>
|
||||
<mat-icon>edit</mat-icon>Edit
|
||||
</button>
|
||||
<button mat-button color="accent" (click)="deleteContact(contact)">
|
||||
<mat-icon>delete</mat-icon>Delete
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
} }
|
||||
<div class="contact__actions">
|
||||
<button mat-raised-button color="primary" (click)="openCreateNew($event)">
|
||||
<mat-icon>add</mat-icon>Create a Contact
|
||||
@@ -45,3 +45,4 @@
|
||||
#contactDetailsWrapper
|
||||
></app-dialog-bottom-sheet-wrapper>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, ViewChild } from '@angular/core';
|
||||
import { Component, ViewChild, computed } from '@angular/core';
|
||||
import { Contact, ContactService } from './contact.service';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
@@ -70,9 +70,9 @@ export class ContactDetailsDialogComponent implements DialogBottomSheetContent {
|
||||
|
||||
init(params: ContactDetailsParams) {
|
||||
this.params = params;
|
||||
this.contactIndex = this.contactService.contacts.findIndex(
|
||||
(c) => c === params.data.contact
|
||||
);
|
||||
this.contactIndex = this.contactService
|
||||
.contacts()
|
||||
.findIndex((c) => c === params.data.contact);
|
||||
this.contact = JSON.parse(JSON.stringify(params.data.contact));
|
||||
}
|
||||
|
||||
@@ -115,6 +115,16 @@ export class ContactDetailsDialogComponent implements DialogBottomSheetContent {
|
||||
})
|
||||
export default class ContactComponent {
|
||||
public static PATH = 'contact';
|
||||
public groupedContacts = computed(() => {
|
||||
return this.contactService.contacts().reduce((acc, contact) => {
|
||||
contact.types.forEach((contactType) => {
|
||||
acc
|
||||
.find((group: GroupedContacts) => group.value === contactType)
|
||||
?.contacts.push(contact);
|
||||
});
|
||||
return acc;
|
||||
}, JSON.parse(JSON.stringify(contactTypes)));
|
||||
});
|
||||
|
||||
@ViewChild('contactDetailsWrapper')
|
||||
detailsComponentWrapper!: DialogBottomSheetWrapper;
|
||||
@@ -131,17 +141,6 @@ export default class ContactComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public get groupedData() {
|
||||
return this.contactService.contacts?.reduce((acc, contact) => {
|
||||
contact.types.forEach((type) => {
|
||||
acc
|
||||
.find((group: GroupedContacts) => group.value === type)
|
||||
?.contacts.push(contact);
|
||||
});
|
||||
return acc;
|
||||
}, JSON.parse(JSON.stringify(contactTypes)));
|
||||
}
|
||||
|
||||
deleteContact(contact: Contact) {
|
||||
if (confirm(`Please confirm contact ${contact.name} delete`)) {
|
||||
this.contactService.deleteContact(contact).subscribe({
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, signal } from '@angular/core';
|
||||
import { Observable, tap } from 'rxjs';
|
||||
import { RegistrarService } from 'src/app/registrar/registrar.service';
|
||||
import { BackendService } from 'src/app/shared/services/backend.service';
|
||||
@@ -33,7 +33,7 @@ export interface Contact {
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ContactService {
|
||||
contacts: Contact[] = [];
|
||||
contacts = signal<Contact[]>([]);
|
||||
|
||||
constructor(
|
||||
private backend: BackendService,
|
||||
@@ -42,39 +42,37 @@ export class ContactService {
|
||||
|
||||
// TODO: Come up with a better handling for registrarId
|
||||
fetchContacts(): Observable<Contact[]> {
|
||||
return this.backend
|
||||
.getContacts(this.registrarService.activeRegistrarId)
|
||||
.pipe(
|
||||
tap((contacts = []) => {
|
||||
this.contacts = contacts;
|
||||
})
|
||||
);
|
||||
return this.backend.getContacts(this.registrarService.registrarId()).pipe(
|
||||
tap((contacts = []) => {
|
||||
this.contacts.set(contacts);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
saveContacts(contacts: Contact[]): Observable<Contact[]> {
|
||||
return this.backend
|
||||
.postContacts(this.registrarService.activeRegistrarId, contacts)
|
||||
.postContacts(this.registrarService.registrarId(), contacts)
|
||||
.pipe(
|
||||
tap((_) => {
|
||||
this.contacts = contacts;
|
||||
this.contacts.set(contacts);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
updateContact(index: number, contact: Contact) {
|
||||
const newContacts = this.contacts.map((c, i) =>
|
||||
const newContacts = this.contacts().map((c, i) =>
|
||||
i === index ? contact : c
|
||||
);
|
||||
return this.saveContacts(newContacts);
|
||||
}
|
||||
|
||||
addContact(contact: Contact) {
|
||||
const newContacts = this.contacts.concat([contact]);
|
||||
const newContacts = this.contacts().concat([contact]);
|
||||
return this.saveContacts(newContacts);
|
||||
}
|
||||
|
||||
deleteContact(contact: Contact) {
|
||||
const newContacts = this.contacts.filter((c) => c !== contact);
|
||||
const newContacts = this.contacts().filter((c) => c !== contact);
|
||||
return this.saveContacts(newContacts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ export default class SecurityComponent {
|
||||
private _snackBar: MatSnackBar,
|
||||
public registrarService: RegistrarService
|
||||
) {
|
||||
this.dataSource = apiToUiConverter(this.registrarService.registrar);
|
||||
this.dataSource = apiToUiConverter(this.registrarService.registrar());
|
||||
}
|
||||
|
||||
enableEdit() {
|
||||
@@ -76,6 +76,6 @@ export default class SecurityComponent {
|
||||
}
|
||||
|
||||
resetDataSource() {
|
||||
this.dataSource = apiToUiConverter(this.registrarService.registrar);
|
||||
this.dataSource = apiToUiConverter(this.registrarService.registrar());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ export class SecurityService {
|
||||
saveChanges(newSecuritySettings: SecuritySettings) {
|
||||
return this.backend
|
||||
.postSecuritySettings(
|
||||
this.registrarService.activeRegistrarId,
|
||||
this.registrarService.registrarId(),
|
||||
uiToApiConverter(newSecuritySettings)
|
||||
)
|
||||
.pipe(
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
FROM gcr.io/distroless/java:debug
|
||||
FROM gcr.io/distroless/java17-debian11:debug
|
||||
ADD build/libs/nomulus.jar /nomulus.jar
|
||||
ENTRYPOINT ["/usr/bin/java", "-jar", "/nomulus.jar"]
|
||||
|
||||
@@ -776,7 +776,7 @@ if (environment == 'alpha') {
|
||||
gs://${gcpProject}-deploy/live/beam/${metaDataBaseName} \
|
||||
--image-gcr-path ${imageName}:live \
|
||||
--sdk-language JAVA \
|
||||
--flex-template-base-image JAVA11 \
|
||||
--flex-template-base-image JAVA17 \
|
||||
--metadata-file ${projectDir}/src/main/resources/${metaData} \
|
||||
--jar ${uberJarName} \
|
||||
--env FLEX_TEMPLATE_JAVA_MAIN_CLASS=${mainClass} \
|
||||
|
||||
@@ -207,7 +207,7 @@ public class CloudTasksUtils implements Serializable {
|
||||
Service service,
|
||||
Multimap<String, String> params,
|
||||
Optional<Integer> jitterSeconds) {
|
||||
if (!jitterSeconds.isPresent() || jitterSeconds.get() <= 0) {
|
||||
if (jitterSeconds.isEmpty() || jitterSeconds.get() <= 0) {
|
||||
return createTask(path, method, service, params);
|
||||
}
|
||||
return createTaskWithDelay(
|
||||
|
||||
@@ -171,7 +171,7 @@ public class DeleteExpiredDomainsAction implements Runnable {
|
||||
tm().transact(
|
||||
() -> {
|
||||
Domain transDomain = tm().loadByKey(domain.createVKey());
|
||||
if (!domain.getAutorenewEndTime().isPresent()
|
||||
if (domain.getAutorenewEndTime().isEmpty()
|
||||
|| domain.getAutorenewEndTime().get().isAfter(tm().getTransactionTime())) {
|
||||
logger.atSevere().log(
|
||||
"Failed to delete domain %s because of its autorenew end time: %s.",
|
||||
|
||||
@@ -21,7 +21,6 @@ import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
|
||||
import static org.apache.http.HttpStatus.SC_OK;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -125,7 +124,7 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
return Streams.stream(Registrar.loadAllCached())
|
||||
.map(
|
||||
registrar ->
|
||||
RegistrarInfo.create(
|
||||
new RegistrarInfo(
|
||||
registrar,
|
||||
registrar.getClientCertificate().isPresent()
|
||||
&& certificateChecker.shouldReceiveExpiringNotification(
|
||||
@@ -151,7 +150,7 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
DateTime lastExpiringCertNotificationSentDate,
|
||||
CertificateType certificateType,
|
||||
Optional<String> certificate) {
|
||||
if (!certificate.isPresent()
|
||||
if (certificate.isEmpty()
|
||||
|| !certificateChecker.shouldReceiveExpiringNotification(
|
||||
lastExpiringCertNotificationSentDate, certificate.get())) {
|
||||
return false;
|
||||
@@ -333,19 +332,6 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
public abstract static class RegistrarInfo {
|
||||
|
||||
static RegistrarInfo create(
|
||||
Registrar registrar, boolean isCertExpiring, boolean isFailOverCertExpiring) {
|
||||
return new AutoValue_SendExpiringCertificateNotificationEmailAction_RegistrarInfo(
|
||||
registrar, isCertExpiring, isFailOverCertExpiring);
|
||||
}
|
||||
|
||||
public abstract Registrar registrar();
|
||||
|
||||
public abstract boolean isCertExpiring();
|
||||
|
||||
public abstract boolean isFailOverCertExpiring();
|
||||
}
|
||||
record RegistrarInfo(
|
||||
Registrar registrar, boolean isCertExpiring, boolean isFailOverCertExpiring) {}
|
||||
}
|
||||
|
||||
@@ -21,26 +21,21 @@ import google.registry.reporting.billing.BillingModule;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.beam.sdk.coders.AtomicCoder;
|
||||
import org.apache.beam.sdk.coders.Coder;
|
||||
import org.apache.beam.sdk.coders.DoubleCoder;
|
||||
import org.apache.beam.sdk.coders.NullableCoder;
|
||||
import org.apache.beam.sdk.coders.StringUtf8Coder;
|
||||
import org.apache.beam.sdk.coders.VarIntCoder;
|
||||
import org.apache.beam.sdk.coders.VarLongCoder;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* A POJO representing a single billable event, parsed from a {@code SchemaAndRecord}.
|
||||
*
|
||||
* <p>This is a trivially serializable class that allows Beam to transform the results of a Cloud
|
||||
* SQL query into a standard Java representation, giving us the type guarantees and ease of
|
||||
* manipulation Cloud SQL lacks.
|
||||
*/
|
||||
/** A POJO representing a single billable event, parsed from a {@code SchemaAndRecord}. */
|
||||
@AutoValue
|
||||
public abstract class BillingEvent implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3593088371541450077L;
|
||||
public abstract class BillingEvent {
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER =
|
||||
DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss zzz");
|
||||
@@ -85,7 +80,7 @@ public abstract class BillingEvent implements Serializable {
|
||||
/** Returns the tld this event was generated for. */
|
||||
abstract String tld();
|
||||
|
||||
/** Returns the billable action this event was generated for (i.e. RENEW, CREATE, TRANSFER...) */
|
||||
/** Returns the billable action this event was generated for (i.e., RENEW, CREATE, TRANSFER...) */
|
||||
abstract String action();
|
||||
|
||||
/** Returns the fully qualified domain name this event was generated for. */
|
||||
@@ -97,7 +92,7 @@ public abstract class BillingEvent implements Serializable {
|
||||
/** Returns the number of years this billing event is made out for. */
|
||||
abstract int years();
|
||||
|
||||
/** Returns the 3-letter currency code for the billing event (i.e. USD or JPY.) */
|
||||
/** Returns the 3-letter currency code for the billing event (i.e., USD or JPY.) */
|
||||
abstract String currency();
|
||||
|
||||
/** Returns the cost associated with this billing event. */
|
||||
@@ -203,9 +198,7 @@ public abstract class BillingEvent implements Serializable {
|
||||
|
||||
/** Key for each {@code BillingEvent}, when aggregating for the overall invoice. */
|
||||
@AutoValue
|
||||
abstract static class InvoiceGroupingKey implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -151561764235256205L;
|
||||
abstract static class InvoiceGroupingKey {
|
||||
|
||||
private static final ImmutableList<String> INVOICE_HEADERS =
|
||||
ImmutableList.of(
|
||||
@@ -277,8 +270,14 @@ public abstract class BillingEvent implements Serializable {
|
||||
|
||||
/** Coder that provides deterministic (de)serialization for {@code InvoiceGroupingKey}. */
|
||||
static class InvoiceGroupingKeyCoder extends AtomicCoder<InvoiceGroupingKey> {
|
||||
private static final Coder<String> stringCoder = StringUtf8Coder.of();
|
||||
private static final InvoiceGroupingKeyCoder INSTANCE = new InvoiceGroupingKeyCoder();
|
||||
|
||||
private static final long serialVersionUID = 6680701524304107547L;
|
||||
public static InvoiceGroupingKeyCoder of() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private InvoiceGroupingKeyCoder() {}
|
||||
|
||||
@Override
|
||||
public void encode(InvoiceGroupingKey value, OutputStream outStream) throws IOException {
|
||||
@@ -295,7 +294,6 @@ public abstract class BillingEvent implements Serializable {
|
||||
|
||||
@Override
|
||||
public InvoiceGroupingKey decode(InputStream inStream) throws IOException {
|
||||
Coder<String> stringCoder = StringUtf8Coder.of();
|
||||
return new AutoValue_BillingEvent_InvoiceGroupingKey(
|
||||
stringCoder.decode(inStream),
|
||||
stringCoder.decode(inStream),
|
||||
@@ -308,4 +306,55 @@ public abstract class BillingEvent implements Serializable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class BillingEventCoder extends AtomicCoder<BillingEvent> {
|
||||
private static final Coder<String> stringCoder = StringUtf8Coder.of();
|
||||
private static final Coder<Integer> integerCoder = VarIntCoder.of();
|
||||
private static final Coder<Long> longCoder = VarLongCoder.of();
|
||||
private static final Coder<Double> doubleCoder = DoubleCoder.of();
|
||||
private static final BillingEventCoder INSTANCE = new BillingEventCoder();
|
||||
|
||||
static NullableCoder<BillingEvent> ofNullable() {
|
||||
return NullableCoder.of(INSTANCE);
|
||||
}
|
||||
|
||||
private BillingEventCoder() {}
|
||||
|
||||
@Override
|
||||
public void encode(BillingEvent value, OutputStream outStream) throws IOException {
|
||||
longCoder.encode(value.id(), outStream);
|
||||
stringCoder.encode(DATE_TIME_FORMATTER.print(value.billingTime()), outStream);
|
||||
stringCoder.encode(DATE_TIME_FORMATTER.print(value.eventTime()), outStream);
|
||||
stringCoder.encode(value.registrarId(), outStream);
|
||||
stringCoder.encode(value.billingId(), outStream);
|
||||
stringCoder.encode(value.poNumber(), outStream);
|
||||
stringCoder.encode(value.tld(), outStream);
|
||||
stringCoder.encode(value.action(), outStream);
|
||||
stringCoder.encode(value.domain(), outStream);
|
||||
stringCoder.encode(value.repositoryId(), outStream);
|
||||
integerCoder.encode(value.years(), outStream);
|
||||
stringCoder.encode(value.currency(), outStream);
|
||||
doubleCoder.encode(value.amount(), outStream);
|
||||
stringCoder.encode(value.flags(), outStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BillingEvent decode(InputStream inStream) throws IOException {
|
||||
return new AutoValue_BillingEvent(
|
||||
longCoder.decode(inStream),
|
||||
DATE_TIME_FORMATTER.parseDateTime(stringCoder.decode(inStream)),
|
||||
DATE_TIME_FORMATTER.parseDateTime(stringCoder.decode(inStream)),
|
||||
stringCoder.decode(inStream),
|
||||
stringCoder.decode(inStream),
|
||||
stringCoder.decode(inStream),
|
||||
stringCoder.decode(inStream),
|
||||
stringCoder.decode(inStream),
|
||||
stringCoder.decode(inStream),
|
||||
stringCoder.decode(inStream),
|
||||
integerCoder.decode(inStream),
|
||||
stringCoder.decode(inStream),
|
||||
doubleCoder.decode(inStream),
|
||||
stringCoder.decode(inStream));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static org.apache.beam.sdk.values.TypeDescriptors.strings;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.beam.billing.BillingEvent.BillingEventCoder;
|
||||
import google.registry.beam.billing.BillingEvent.InvoiceGroupingKey;
|
||||
import google.registry.beam.billing.BillingEvent.InvoiceGroupingKey.InvoiceGroupingKeyCoder;
|
||||
import google.registry.beam.common.RegistryJpaIO;
|
||||
@@ -30,6 +31,7 @@ import google.registry.reporting.billing.BillingModule;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import google.registry.util.ResourceUtils;
|
||||
import google.registry.util.SqlTemplate;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.YearMonth;
|
||||
import java.util.Objects;
|
||||
@@ -37,13 +39,13 @@ import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.beam.sdk.Pipeline;
|
||||
import org.apache.beam.sdk.PipelineResult;
|
||||
import org.apache.beam.sdk.coders.SerializableCoder;
|
||||
import org.apache.beam.sdk.coders.StringUtf8Coder;
|
||||
import org.apache.beam.sdk.io.FileIO;
|
||||
import org.apache.beam.sdk.io.TextIO;
|
||||
import org.apache.beam.sdk.options.PipelineOptionsFactory;
|
||||
import org.apache.beam.sdk.transforms.Contextful;
|
||||
import org.apache.beam.sdk.transforms.Count;
|
||||
import org.apache.beam.sdk.transforms.Distinct;
|
||||
import org.apache.beam.sdk.transforms.Filter;
|
||||
import org.apache.beam.sdk.transforms.MapElements;
|
||||
import org.apache.beam.sdk.transforms.PTransform;
|
||||
@@ -65,7 +67,7 @@ import org.joda.money.CurrencyUnit;
|
||||
*/
|
||||
public class InvoicingPipeline implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 5386330443625580081L;
|
||||
@Serial private static final long serialVersionUID = 5386330443625580081L;
|
||||
|
||||
private static final Pattern SQL_COMMENT_REGEX =
|
||||
Pattern.compile("^\\s*--.*\\n", Pattern.MULTILINE);
|
||||
@@ -97,13 +99,11 @@ public class InvoicingPipeline implements Serializable {
|
||||
Read<Object[], google.registry.beam.billing.BillingEvent> read =
|
||||
RegistryJpaIO.<Object[], google.registry.beam.billing.BillingEvent>read(
|
||||
makeCloudSqlQuery(options.getYearMonth()), false, row -> parseRow(row).orElse(null))
|
||||
.withCoder(SerializableCoder.of(google.registry.beam.billing.BillingEvent.class));
|
||||
|
||||
PCollection<google.registry.beam.billing.BillingEvent> billingEventsWithNulls =
|
||||
pipeline.apply("Read BillingEvents from Cloud SQL", read);
|
||||
|
||||
// Remove null billing events
|
||||
return billingEventsWithNulls.apply(Filter.by(Objects::nonNull));
|
||||
.withCoder(BillingEventCoder.ofNullable());
|
||||
return pipeline
|
||||
.apply("Read BillingEvents from Cloud SQL", read)
|
||||
.apply("Remove null elements", Filter.by(Objects::nonNull))
|
||||
.apply("Remove duplicates", Distinct.create());
|
||||
}
|
||||
|
||||
private static Optional<google.registry.beam.billing.BillingEvent> parseRow(Object[] row) {
|
||||
@@ -142,7 +142,7 @@ public class InvoicingPipeline implements Serializable {
|
||||
extends PTransform<
|
||||
PCollection<google.registry.beam.billing.BillingEvent>, PCollection<String>> {
|
||||
|
||||
private static final long serialVersionUID = -8090619008258393728L;
|
||||
@Serial private static final long serialVersionUID = -8090619008258393728L;
|
||||
|
||||
@Override
|
||||
public PCollection<String> expand(
|
||||
@@ -152,9 +152,9 @@ public class InvoicingPipeline implements Serializable {
|
||||
"Map to invoicing key",
|
||||
MapElements.into(TypeDescriptor.of(InvoiceGroupingKey.class))
|
||||
.via(google.registry.beam.billing.BillingEvent::getInvoiceGroupingKey))
|
||||
.setCoder(InvoiceGroupingKeyCoder.of())
|
||||
.apply(
|
||||
"Filter out free events", Filter.by((InvoiceGroupingKey key) -> key.unitPrice() != 0))
|
||||
.setCoder(new InvoiceGroupingKeyCoder())
|
||||
.apply("Count occurrences", Count.perElement())
|
||||
.apply(
|
||||
"Format as CSVs",
|
||||
@@ -163,7 +163,12 @@ public class InvoicingPipeline implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/** Saves the billing events to a single overall invoice CSV file. */
|
||||
/**
|
||||
* Saves the billing events to a single overall invoice CSV file. TextIO always produces the file
|
||||
* of type text/plain, which we then update to desired text/csv before sending an email to billing
|
||||
* team {@link google.registry.reporting.billing.BillingEmailUtils#emailOverallInvoice()
|
||||
* emailOverallInvoice}
|
||||
*/
|
||||
static void saveInvoiceCsv(
|
||||
PCollection<google.registry.beam.billing.BillingEvent> billingEvents,
|
||||
InvoicingPipelineOptions options) {
|
||||
|
||||
@@ -209,9 +209,16 @@ public final class RegistryJpaIO {
|
||||
|
||||
@ProcessElement
|
||||
public void processElement(OutputReceiver<T> outputReceiver) {
|
||||
tm().transact(
|
||||
// Note the use of no-retry transaction here. The results from the query are streamed to the
|
||||
// output receiver inside the transaction, which cannot be rolled back in case of a retry,
|
||||
// which in turn results in duplicate elements. If we try to pass the results to the output
|
||||
// receiver outside the transaction, they have to be materialized into a list containing all
|
||||
// the elements (without resorting to manual pagination) and greatly decrease the
|
||||
// parallelism of the pipeline.
|
||||
tm().transactNoRetry(
|
||||
() -> {
|
||||
query.stream().map(resultMapper::apply).forEach(outputReceiver::output);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ package google.registry.beam.spec11;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import dagger.Component;
|
||||
import dagger.Module;
|
||||
@@ -199,7 +198,7 @@ public class Spec11Pipeline implements Serializable {
|
||||
(KV<DomainNameInfo, ThreatMatch> kv) ->
|
||||
KV.of(
|
||||
kv.getKey().registrarId(),
|
||||
EmailAndThreatMatch.create(
|
||||
new EmailAndThreatMatch(
|
||||
kv.getKey().registrarEmailAddress(), kv.getValue()))))
|
||||
.apply("Group by registrar client ID", GroupByKey.create())
|
||||
.apply(
|
||||
@@ -281,15 +280,5 @@ public class Spec11Pipeline implements Serializable {
|
||||
Spec11Pipeline spec11Pipeline();
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
abstract static class EmailAndThreatMatch implements Serializable {
|
||||
|
||||
abstract String email();
|
||||
|
||||
abstract ThreatMatch threatMatch();
|
||||
|
||||
static EmailAndThreatMatch create(String email, ThreatMatch threatMatch) {
|
||||
return new AutoValue_Spec11Pipeline_EmailAndThreatMatch(email, threatMatch);
|
||||
}
|
||||
}
|
||||
record EmailAndThreatMatch(String email, ThreatMatch threatMatch) implements Serializable {}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ public class BsaDownloadAction implements Runnable {
|
||||
return null;
|
||||
}
|
||||
Optional<DownloadSchedule> scheduleOptional = downloadScheduler.schedule();
|
||||
if (!scheduleOptional.isPresent()) {
|
||||
if (scheduleOptional.isEmpty()) {
|
||||
logger.atInfo().log("Nothing to do.");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public class BsaRefreshAction implements Runnable {
|
||||
private final GcsClient gcsClient;
|
||||
private final BsaReportSender bsaReportSender;
|
||||
private final int transactionBatchSize;
|
||||
private final Duration domainTxnMaxDuration;
|
||||
private final Duration domainCreateTxnCommitTimeLag;
|
||||
private final BsaLock bsaLock;
|
||||
private final Clock clock;
|
||||
private final Response response;
|
||||
@@ -65,7 +65,7 @@ public class BsaRefreshAction implements Runnable {
|
||||
GcsClient gcsClient,
|
||||
BsaReportSender bsaReportSender,
|
||||
@Config("bsaTxnBatchSize") int transactionBatchSize,
|
||||
@Config("domainCreateTxnCommitTimeLag") Duration domainTxnMaxDuration,
|
||||
@Config("domainCreateTxnCommitTimeLag") Duration domainCreateTxnCommitTimeLag,
|
||||
BsaLock bsaLock,
|
||||
Clock clock,
|
||||
Response response) {
|
||||
@@ -73,7 +73,7 @@ public class BsaRefreshAction implements Runnable {
|
||||
this.gcsClient = gcsClient;
|
||||
this.bsaReportSender = bsaReportSender;
|
||||
this.transactionBatchSize = transactionBatchSize;
|
||||
this.domainTxnMaxDuration = domainTxnMaxDuration;
|
||||
this.domainCreateTxnCommitTimeLag = domainCreateTxnCommitTimeLag;
|
||||
this.bsaLock = bsaLock;
|
||||
this.clock = clock;
|
||||
this.response = response;
|
||||
@@ -102,14 +102,17 @@ public class BsaRefreshAction implements Runnable {
|
||||
return null;
|
||||
}
|
||||
Optional<RefreshSchedule> maybeSchedule = scheduler.schedule();
|
||||
if (!maybeSchedule.isPresent()) {
|
||||
if (maybeSchedule.isEmpty()) {
|
||||
logger.atInfo().log("No completed downloads yet. Exiting.");
|
||||
return null;
|
||||
}
|
||||
RefreshSchedule schedule = maybeSchedule.get();
|
||||
DomainsRefresher refresher =
|
||||
new DomainsRefresher(
|
||||
schedule.prevRefreshTime(), clock.nowUtc(), domainTxnMaxDuration, transactionBatchSize);
|
||||
schedule.prevRefreshTime(),
|
||||
clock.nowUtc(),
|
||||
domainCreateTxnCommitTimeLag,
|
||||
transactionBatchSize);
|
||||
switch (schedule.stage()) {
|
||||
case CHECK_FOR_CHANGES:
|
||||
ImmutableList<UnblockableDomainChange> blockabilityChanges =
|
||||
@@ -132,10 +135,12 @@ public class BsaRefreshAction implements Runnable {
|
||||
case UPLOAD_REMOVALS:
|
||||
try (Stream<UnblockableDomainChange> changes =
|
||||
gcsClient.readRefreshChanges(schedule.jobName())) {
|
||||
// Unblockables with changes in REASON are removed then added back. That is why they are
|
||||
// included in this stage.
|
||||
Optional<String> report =
|
||||
JsonSerializations.toUnblockableDomainsRemovalReport(
|
||||
changes
|
||||
.filter(UnblockableDomainChange::isDelete)
|
||||
.filter(UnblockableDomainChange::isChangeOrDelete)
|
||||
.map(UnblockableDomainChange::domainName));
|
||||
if (report.isPresent()) {
|
||||
gcsClient.logRemovedUnblockableDomainsReport(
|
||||
@@ -153,12 +158,12 @@ public class BsaRefreshAction implements Runnable {
|
||||
Optional<String> report =
|
||||
JsonSerializations.toUnblockableDomainsReport(
|
||||
changes
|
||||
.filter(UnblockableDomainChange::isAddOrChange)
|
||||
.filter(UnblockableDomainChange::isNewOrChange)
|
||||
.map(UnblockableDomainChange::newValue));
|
||||
if (report.isPresent()) {
|
||||
gcsClient.logRemovedUnblockableDomainsReport(
|
||||
gcsClient.logAddedUnblockableDomainsReport(
|
||||
schedule.jobName(), LINE_SPLITTER.splitToStream(report.get()));
|
||||
bsaReportSender.removeUnblockableDomainsUpdates(report.get());
|
||||
bsaReportSender.addUnblockableDomainsUpdates(report.get());
|
||||
} else {
|
||||
logger.atInfo().log("No new Unblockable domains to add.");
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public class BsaStringUtils {
|
||||
public static final Splitter LINE_SPLITTER = Splitter.on('\n');
|
||||
|
||||
public static String getLabelInDomain(String domainName) {
|
||||
List<String> parts = DOMAIN_SPLITTER.limit(1).splitToList(domainName);
|
||||
List<String> parts = DOMAIN_SPLITTER.splitToList(domainName);
|
||||
checkArgument(!parts.isEmpty(), "Not a valid domain: [%s]", domainName);
|
||||
return parts.get(0);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import google.registry.persistence.transaction.TransactionManager.ThrowingRunnable;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
@@ -36,13 +37,19 @@ public final class BsaTransactions {
|
||||
@CanIgnoreReturnValue
|
||||
public static <T> T bsaTransact(Callable<T> work) {
|
||||
verify(!isInTransaction(), "May only be used for top-level transactions.");
|
||||
return tm().transact(work, TRANSACTION_REPEATABLE_READ);
|
||||
return tm().transact(TRANSACTION_REPEATABLE_READ, work);
|
||||
}
|
||||
|
||||
public static void bsaTransact(ThrowingRunnable work) {
|
||||
verify(!isInTransaction(), "May only be used for top-level transactions.");
|
||||
tm().transact(TRANSACTION_REPEATABLE_READ, work);
|
||||
}
|
||||
|
||||
@CanIgnoreReturnValue
|
||||
public static <T> T bsaQuery(Callable<T> work) {
|
||||
verify(!isInTransaction(), "May only be used for top-level transactions.");
|
||||
return replicaTm().transact(work, TRANSACTION_REPEATABLE_READ);
|
||||
// TRANSACTION_REPEATABLE_READ is default on replica.
|
||||
return replicaTm().transact(work);
|
||||
}
|
||||
|
||||
private BsaTransactions() {}
|
||||
|
||||
@@ -161,7 +161,7 @@ public class GcsClient {
|
||||
}
|
||||
|
||||
Stream<UnblockableDomainChange> readRefreshChanges(String jobName) {
|
||||
BlobId blobId = getBlobId(jobName, UNBLOCKABLE_DOMAINS_FILE);
|
||||
BlobId blobId = getBlobId(jobName, REFRESHED_UNBLOCKABLE_DOMAINS_FILE);
|
||||
return readStream(blobId).map(UnblockableDomainChange::deserialize);
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final int BATCH_SIZE = 50000;
|
||||
private static final int BATCH_SIZE = 40000;
|
||||
|
||||
Clock clock;
|
||||
|
||||
|
||||
@@ -18,19 +18,13 @@ import static google.registry.bsa.BsaStringUtils.DOMAIN_JOINER;
|
||||
import static google.registry.bsa.BsaStringUtils.PROPERTY_JOINER;
|
||||
import static google.registry.bsa.BsaStringUtils.PROPERTY_SPLITTER;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A domain name whose second-level domain (SLD) matches a BSA label but is not blocked. It may be
|
||||
* already registered, or on the TLD's reserve list.
|
||||
*/
|
||||
// TODO(1/15/2024): rename to UnblockableDomain.
|
||||
@AutoValue
|
||||
public abstract class UnblockableDomain {
|
||||
abstract String domainName();
|
||||
|
||||
abstract Reason reason();
|
||||
public record UnblockableDomain(String domainName, Reason reason) {
|
||||
|
||||
/** Reasons why a valid domain name cannot be blocked. */
|
||||
public enum Reason {
|
||||
@@ -45,14 +39,10 @@ public abstract class UnblockableDomain {
|
||||
|
||||
public static UnblockableDomain deserialize(String text) {
|
||||
List<String> items = PROPERTY_SPLITTER.splitToList(text);
|
||||
return of(items.get(0), Reason.valueOf(items.get(1)));
|
||||
}
|
||||
|
||||
public static UnblockableDomain of(String domainName, Reason reason) {
|
||||
return new AutoValue_UnblockableDomain(domainName, reason);
|
||||
return new UnblockableDomain(items.get(0), Reason.valueOf(items.get(1)));
|
||||
}
|
||||
|
||||
public static UnblockableDomain of(String label, String tld, Reason reason) {
|
||||
return of(DOMAIN_JOINER.join(label, tld), reason);
|
||||
return new UnblockableDomain(DOMAIN_JOINER.join(label, tld), reason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,15 +49,19 @@ public abstract class UnblockableDomainChange {
|
||||
@Memoized
|
||||
public UnblockableDomain newValue() {
|
||||
verify(newReason().isPresent(), "Removed unblockable does not have new value.");
|
||||
return UnblockableDomain.of(unblockable().domainName(), newReason().get());
|
||||
return new UnblockableDomain(unblockable().domainName(), newReason().get());
|
||||
}
|
||||
|
||||
public boolean isAddOrChange() {
|
||||
public boolean isNewOrChange() {
|
||||
return newReason().isPresent();
|
||||
}
|
||||
|
||||
public boolean isChangeOrDelete() {
|
||||
return !isNew();
|
||||
}
|
||||
|
||||
public boolean isDelete() {
|
||||
return !this.isAddOrChange();
|
||||
return !this.isNewOrChange();
|
||||
}
|
||||
|
||||
public boolean isNew() {
|
||||
@@ -74,7 +78,7 @@ public abstract class UnblockableDomainChange {
|
||||
public static UnblockableDomainChange deserialize(String text) {
|
||||
List<String> items = BsaStringUtils.PROPERTY_SPLITTER.splitToList(text);
|
||||
return of(
|
||||
UnblockableDomain.of(items.get(0), Reason.valueOf(items.get(1))),
|
||||
new UnblockableDomain(items.get(0), Reason.valueOf(items.get(1))),
|
||||
Objects.equals(items.get(2), DELETE_REASON_PLACEHOLDER)
|
||||
? Optional.empty()
|
||||
: Optional.of(Reason.valueOf(items.get(2))));
|
||||
|
||||
@@ -105,6 +105,10 @@ class BsaUnblockableDomain {
|
||||
return new BsaUnblockableDomain(parts.get(0), parts.get(1), reason);
|
||||
}
|
||||
|
||||
static BsaUnblockableDomain of(UnblockableDomain unblockable) {
|
||||
return of(unblockable.domainName(), Reason.valueOf(unblockable.reason().name()));
|
||||
}
|
||||
|
||||
static VKey<BsaUnblockableDomain> vKey(String domainName) {
|
||||
ImmutableList<String> parts = ImmutableList.copyOf(DOMAIN_SPLITTER.splitToList(domainName));
|
||||
verify(parts.size() == 2, "Invalid domain name: %s", domainName);
|
||||
|
||||
@@ -18,6 +18,7 @@ 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.bsa.BsaTransactions.bsaQuery;
|
||||
import static google.registry.bsa.BsaTransactions.bsaTransact;
|
||||
import static google.registry.bsa.ReservedDomainsUtils.getAllReservedNames;
|
||||
import static google.registry.bsa.ReservedDomainsUtils.isReservedDomain;
|
||||
import static google.registry.bsa.persistence.Queries.batchReadUnblockables;
|
||||
@@ -44,6 +45,7 @@ import google.registry.model.domain.Domain;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.util.BatchedStreams;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -107,6 +109,8 @@ public final class DomainsRefresher {
|
||||
|
||||
ImmutableSet<String> upgradedDomains =
|
||||
upgrades.stream().map(UnblockableDomainChange::domainName).collect(toImmutableSet());
|
||||
// Upgrades are collected in its own txn after downgrades so need reconciliation. Conflicts are
|
||||
// unlikely but possible: domains may be recreated or reserved names added during the interval.
|
||||
ImmutableList<UnblockableDomainChange> trueDowngrades =
|
||||
downgrades.stream()
|
||||
.filter(c -> !upgradedDomains.contains(c.domainName()))
|
||||
@@ -198,22 +202,50 @@ public final class DomainsRefresher {
|
||||
}
|
||||
|
||||
public ImmutableList<UnblockableDomainChange> getNewUnblockables() {
|
||||
return bsaQuery(
|
||||
() -> {
|
||||
// TODO(weiminyu): both methods below use `queryBsaLabelByLabels`. Should combine.
|
||||
ImmutableSet<String> newCreated = getNewlyCreatedUnblockables(prevRefreshStartTime, now);
|
||||
ImmutableSet<String> newReserved =
|
||||
getNewlyReservedUnblockables(now, transactionBatchSize);
|
||||
SetView<String> reservedNotCreated = Sets.difference(newReserved, newCreated);
|
||||
return Streams.concat(
|
||||
newCreated.stream()
|
||||
.map(name -> UnblockableDomain.of(name, Reason.REGISTERED))
|
||||
.map(UnblockableDomainChange::ofNew),
|
||||
reservedNotCreated.stream()
|
||||
.map(name -> UnblockableDomain.of(name, Reason.RESERVED))
|
||||
.map(UnblockableDomainChange::ofNew))
|
||||
.collect(toImmutableList());
|
||||
});
|
||||
// TODO(weiminyu): both methods below use `queryBsaLabelByLabels`. Should combine in a single
|
||||
// query.
|
||||
HashSet<String> newCreated =
|
||||
Sets.newHashSet(bsaQuery(() -> getNewlyCreatedUnblockables(prevRefreshStartTime, now)));
|
||||
// We cannot identify new reserved unblockables so must look at all of them. There are not many
|
||||
// of these.
|
||||
HashSet<String> allReserved =
|
||||
Sets.newHashSet(bsaQuery(() -> getAllReservedUnblockables(now, transactionBatchSize)));
|
||||
HashSet<String> reservedNotCreated = Sets.newHashSet(Sets.difference(allReserved, newCreated));
|
||||
|
||||
ImmutableList.Builder<UnblockableDomainChange> changes = new ImmutableList.Builder<>();
|
||||
ImmutableList<BsaUnblockableDomain> batch;
|
||||
Optional<BsaUnblockableDomain> lastRead = Optional.empty();
|
||||
do {
|
||||
batch = batchReadUnblockables(lastRead, transactionBatchSize);
|
||||
if (batch.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
lastRead = Optional.of(batch.get(batch.size() - 1));
|
||||
for (BsaUnblockableDomain unblockable : batch) {
|
||||
String domainName = unblockable.domainName();
|
||||
if (unblockable.reason.equals(BsaUnblockableDomain.Reason.REGISTERED)) {
|
||||
newCreated.remove(domainName);
|
||||
reservedNotCreated.remove(domainName);
|
||||
} else {
|
||||
reservedNotCreated.remove(domainName);
|
||||
if (newCreated.remove(domainName)) {
|
||||
changes.add(
|
||||
UnblockableDomainChange.ofChanged(
|
||||
unblockable.toUnblockableDomain(), Reason.REGISTERED));
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (batch.size() == transactionBatchSize);
|
||||
|
||||
Streams.concat(
|
||||
newCreated.stream()
|
||||
.map(name -> new UnblockableDomain(name, Reason.REGISTERED))
|
||||
.map(UnblockableDomainChange::ofNew),
|
||||
reservedNotCreated.stream()
|
||||
.map(name -> new UnblockableDomain(name, Reason.RESERVED))
|
||||
.map(UnblockableDomainChange::ofNew))
|
||||
.forEach(changes::add);
|
||||
return changes.build();
|
||||
}
|
||||
|
||||
static ImmutableSet<String> getNewlyCreatedUnblockables(
|
||||
@@ -225,18 +257,18 @@ public final class DomainsRefresher {
|
||||
.collect(toImmutableSet());
|
||||
ImmutableSet<String> liveDomains =
|
||||
queryNewlyCreatedDomains(bsaEnabledTlds, prevRefreshStartTime, now);
|
||||
return getUnblockedDomainNames(liveDomains);
|
||||
return getBlockedDomainNames(liveDomains);
|
||||
}
|
||||
|
||||
static ImmutableSet<String> getNewlyReservedUnblockables(DateTime now, int batchSize) {
|
||||
static ImmutableSet<String> getAllReservedUnblockables(DateTime now, int batchSize) {
|
||||
Stream<String> allReserved = getAllReservedNames(now);
|
||||
return BatchedStreams.toBatches(allReserved, batchSize)
|
||||
.map(DomainsRefresher::getUnblockedDomainNames)
|
||||
.map(DomainsRefresher::getBlockedDomainNames)
|
||||
.flatMap(ImmutableSet::stream)
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
static ImmutableSet<String> getUnblockedDomainNames(ImmutableCollection<String> domainNames) {
|
||||
static ImmutableSet<String> getBlockedDomainNames(ImmutableCollection<String> domainNames) {
|
||||
Map<String, List<String>> labelToNames =
|
||||
domainNames.stream().collect(groupingBy(BsaStringUtils::getLabelInDomain));
|
||||
ImmutableSet<String> bsaLabels =
|
||||
@@ -244,7 +276,7 @@ public final class DomainsRefresher {
|
||||
.map(BsaLabel::getLabel)
|
||||
.collect(toImmutableSet());
|
||||
return labelToNames.entrySet().stream()
|
||||
.filter(entry -> !bsaLabels.contains(entry.getKey()))
|
||||
.filter(entry -> bsaLabels.contains(entry.getKey()))
|
||||
.map(Entry::getValue)
|
||||
.flatMap(List::stream)
|
||||
.collect(toImmutableSet());
|
||||
@@ -257,20 +289,21 @@ public final class DomainsRefresher {
|
||||
.collect(
|
||||
groupingBy(
|
||||
change -> change.isDelete() ? "remove" : "change", toImmutableSet())));
|
||||
tm().transact(
|
||||
() -> {
|
||||
if (changesByType.containsKey("remove")) {
|
||||
tm().delete(
|
||||
changesByType.get("remove").stream()
|
||||
.map(c -> BsaUnblockableDomain.vKey(c.domainName()))
|
||||
.collect(toImmutableSet()));
|
||||
}
|
||||
if (changesByType.containsKey("change")) {
|
||||
tm().putAll(
|
||||
changesByType.get("change").stream()
|
||||
.map(UnblockableDomainChange::newValue)
|
||||
.collect(toImmutableSet()));
|
||||
}
|
||||
});
|
||||
bsaTransact(
|
||||
() -> {
|
||||
if (changesByType.containsKey("remove")) {
|
||||
tm().delete(
|
||||
changesByType.get("remove").stream()
|
||||
.map(c -> BsaUnblockableDomain.vKey(c.domainName()))
|
||||
.collect(toImmutableSet()));
|
||||
}
|
||||
if (changesByType.containsKey("change")) {
|
||||
tm().putAll(
|
||||
changesByType.get("change").stream()
|
||||
.map(UnblockableDomainChange::newValue)
|
||||
.map(BsaUnblockableDomain::of)
|
||||
.collect(toImmutableSet()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ public final class LabelDiffUpdates {
|
||||
labels.stream().collect(groupingBy(BlockLabel::labelType, toImmutableList())));
|
||||
|
||||
tm().transact(
|
||||
TRANSACTION_REPEATABLE_READ,
|
||||
() -> {
|
||||
for (Map.Entry<LabelType, ImmutableList<BlockLabel>> entry :
|
||||
labelsByType.entrySet()) {
|
||||
@@ -128,8 +129,7 @@ public final class LabelDiffUpdates {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
TRANSACTION_REPEATABLE_READ);
|
||||
});
|
||||
logger.atInfo().log("Processed %s of labels.", labels.size());
|
||||
return nonBlockedDomains.build();
|
||||
}
|
||||
@@ -152,7 +152,7 @@ public final class LabelDiffUpdates {
|
||||
ImmutableSet<String> registeredDomainNames =
|
||||
ImmutableSet.copyOf(ForeignKeyUtils.load(Domain.class, validDomainNames, now).keySet());
|
||||
for (String domain : registeredDomainNames) {
|
||||
nonBlockedDomains.add(UnblockableDomain.of(domain, Reason.REGISTERED));
|
||||
nonBlockedDomains.add(new UnblockableDomain(domain, Reason.REGISTERED));
|
||||
tm().put(BsaUnblockableDomain.of(domain, BsaUnblockableDomain.Reason.REGISTERED));
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ public final class LabelDiffUpdates {
|
||||
.filter(domain -> isReservedDomain(domain, now))
|
||||
.collect(toImmutableSet());
|
||||
for (String domain : reservedDomainNames) {
|
||||
nonBlockedDomains.add(UnblockableDomain.of(domain, Reason.RESERVED));
|
||||
nonBlockedDomains.add(new UnblockableDomain(domain, Reason.RESERVED));
|
||||
tm().put(BsaUnblockableDomain.of(domain, BsaUnblockableDomain.Reason.RESERVED));
|
||||
}
|
||||
return nonBlockedDomains.build();
|
||||
|
||||
@@ -52,7 +52,7 @@ public class RefreshScheduler {
|
||||
}
|
||||
// No previously completed refreshes. Need start time of a completed download as
|
||||
// lower bound of refresh checks.
|
||||
if (!mostRecentDownload.isPresent()) {
|
||||
if (mostRecentDownload.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
|
||||
@@ -721,6 +721,17 @@ public final class RegistryConfig {
|
||||
return "gs://" + billingBucket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns origin part of the URL of the billing invoice file
|
||||
*
|
||||
* @see google.registry.beam.billing.InvoicingPipeline
|
||||
*/
|
||||
@Provides
|
||||
@Config("billingInvoiceOriginUrl")
|
||||
public static String provideBillingInvoiceOriginUrl(RegistryConfigSettings config) {
|
||||
return config.billing.billingInvoiceOriginUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not we should publish invoices to partners automatically by default.
|
||||
*
|
||||
|
||||
@@ -173,6 +173,7 @@ public class RegistryConfigSettings {
|
||||
public List<String> invoiceEmailRecipients;
|
||||
public String invoiceReplyToEmailAddress;
|
||||
public String invoiceFilePrefix;
|
||||
public String billingInvoiceOriginUrl;
|
||||
}
|
||||
|
||||
/** Configuration for Registry Data Escrow (RDE). */
|
||||
|
||||
@@ -381,6 +381,7 @@ billing:
|
||||
# Optional return address that overrides the default.
|
||||
invoiceReplyToEmailAddress: null
|
||||
invoiceFilePrefix: REG-INV
|
||||
billingInvoiceOriginUrl: https://billing-origin-url/
|
||||
|
||||
rde:
|
||||
# URL prefix of ICANN's server to upload RDE reports to. Nomulus adds /TLD/ID
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# Example of a TLD configuration file. TLD configuration files are stored in
|
||||
# the internal repository and synced regularly to the database using the
|
||||
# release/cloudbuild-tld-sync.yaml job. The file can be created using the exact
|
||||
# output from the getTldCommand of an existing TLD.
|
||||
|
||||
addGracePeriodLength: "PT432000S"
|
||||
allowedFullyQualifiedHostNames: []
|
||||
allowedRegistrantContactIds: []
|
||||
anchorTenantAddGracePeriodLength: "PT2592000S"
|
||||
autoRenewGracePeriodLength: "PT3888000S"
|
||||
automaticTransferLength: "PT432000S"
|
||||
claimsPeriodEnd: "294247-01-10T04:00:54.775Z"
|
||||
createBillingCost:
|
||||
currency: "USD"
|
||||
amount: 8.00
|
||||
creationTime: "2017-01-03T19:52:47.770Z"
|
||||
currency: "USD"
|
||||
defaultPromoTokens: []
|
||||
dnsAPlusAaaaTtl: null
|
||||
dnsDsTtl: null
|
||||
dnsNsTtl: null
|
||||
dnsPaused: true
|
||||
dnsWriters:
|
||||
- "VoidDnsWriter"
|
||||
driveFolderId: null
|
||||
eapFeeSchedule:
|
||||
"1970-01-01T00:00:00.000Z":
|
||||
currency: "USD"
|
||||
amount: 0.00
|
||||
escrowEnabled: false
|
||||
idnTables: []
|
||||
invoicingEnabled: false
|
||||
lordnUsername: null
|
||||
numDnsPublishLocks: 1
|
||||
pendingDeleteLength: "PT432000S"
|
||||
premiumListName: null
|
||||
pricingEngineClassName: "google.registry.model.pricing.StaticPremiumListPricingEngine"
|
||||
redemptionGracePeriodLength: "PT2592000S"
|
||||
registryLockOrUnlockBillingCost:
|
||||
currency: "USD"
|
||||
amount: 0.00
|
||||
renewBillingCostTransitions:
|
||||
"1970-01-01T00:00:00.000Z":
|
||||
currency: "USD"
|
||||
amount: 8.00
|
||||
renewGracePeriodLength: "PT432000S"
|
||||
reservedListNames: []
|
||||
restoreBillingCost:
|
||||
currency: "USD"
|
||||
amount: 50.00
|
||||
roidSuffix: "EXAMPLE"
|
||||
serverStatusChangeBillingCost:
|
||||
currency: "USD"
|
||||
amount: 0.00
|
||||
tldStateTransitions:
|
||||
"1970-01-01T00:00:00.000Z": "GENERAL_AVAILABILITY"
|
||||
tldStr: "example"
|
||||
tldType: "TEST"
|
||||
tldUnicode: "example"
|
||||
transferGracePeriodLength: "PT432000S"
|
||||
@@ -128,7 +128,7 @@ public class CloudDnsWriter extends BaseDnsWriter {
|
||||
// Return early if no DNS records should be published.
|
||||
// desiredRecordsBuilder is populated with an empty set to indicate that all existing records
|
||||
// should be deleted.
|
||||
if (!domain.isPresent() || !domain.get().shouldPublishToDns()) {
|
||||
if (domain.isEmpty() || !domain.get().shouldPublishToDns()) {
|
||||
desiredRecords.put(absoluteDomainName, ImmutableSet.of());
|
||||
return;
|
||||
}
|
||||
@@ -192,7 +192,7 @@ public class CloudDnsWriter extends BaseDnsWriter {
|
||||
Optional<Host> host = loadByForeignKey(Host.class, hostName, clock.nowUtc());
|
||||
|
||||
// Return early if the host is deleted.
|
||||
if (!host.isPresent()) {
|
||||
if (host.isEmpty()) {
|
||||
desiredRecords.put(absoluteHostName, ImmutableSet.of());
|
||||
return;
|
||||
}
|
||||
@@ -247,7 +247,7 @@ public class CloudDnsWriter extends BaseDnsWriter {
|
||||
Optional<InternetDomainName> tld = Tlds.findTldForName(host);
|
||||
|
||||
// Host not managed by our registry, no need to update DNS.
|
||||
if (!tld.isPresent()) {
|
||||
if (tld.isEmpty()) {
|
||||
logger.atSevere().log("publishHost called for invalid host '%s'.", hostName);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ public class DnsUpdateWriter extends BaseDnsWriter {
|
||||
Optional<InternetDomainName> tld = Tlds.findTldForName(host);
|
||||
|
||||
// host not managed by our registry, no need to update DNS.
|
||||
if (!tld.isPresent()) {
|
||||
if (tld.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>backend</service>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>bsa</service>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>default</service>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
|
||||
@@ -285,6 +285,18 @@
|
||||
<schedule>0 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/bsaRefresh]]></url>
|
||||
<name>bsaRefresh</name>
|
||||
<service>bsa</service>
|
||||
<description>
|
||||
Checks for changes in registered domains and reserved labels, and updates
|
||||
the unblockable domains list.
|
||||
</description>
|
||||
<!-- Runs every 30 minutes. -->
|
||||
<schedule>15,45 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/uploadBsaUnavailableNames]]></url>
|
||||
<name>uploadBsaUnavailableNames</name>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>pubapi</service>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>tools</service>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>backend</service>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>bsa</service>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>default</service>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
|
||||
@@ -174,6 +174,18 @@
|
||||
<schedule>0 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/bsaRefresh]]></url>
|
||||
<name>bsaRefresh</name>
|
||||
<service>bsa</service>
|
||||
<description>
|
||||
Checks for changes in registered domains and reserved labels, and updates
|
||||
the unblockable domains list.
|
||||
</description>
|
||||
<!-- Runs every 30 minutes. -->
|
||||
<schedule>15,45 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/uploadBsaUnavailableNames]]></url>
|
||||
<name>uploadBsaUnavailableNames</name>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>pubapi</service>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>tools</service>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -115,7 +115,7 @@ public class ExportPremiumTermsAction implements Runnable {
|
||||
"Skipping premium terms export for TLD %s because Drive folder isn't specified.", tldStr);
|
||||
return Optional.of("Skipping export because no Drive folder is associated with this TLD");
|
||||
}
|
||||
if (!tld.getPremiumListName().isPresent()) {
|
||||
if (tld.getPremiumListName().isEmpty()) {
|
||||
logger.atInfo().log("No premium terms to export for TLD '%s'.", tldStr);
|
||||
return Optional.of("No premium lists configured");
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ class SyncRegistrarsSheet {
|
||||
boolean wereRegistrarsModified() {
|
||||
Optional<Cursor> cursor =
|
||||
tm().transact(() -> tm().loadByKeyIfPresent(Cursor.createGlobalVKey(SYNC_REGISTRAR_SHEET)));
|
||||
DateTime lastUpdateTime = !cursor.isPresent() ? START_OF_TIME : cursor.get().getCursorTime();
|
||||
DateTime lastUpdateTime = cursor.isEmpty() ? START_OF_TIME : cursor.get().getCursorTime();
|
||||
for (Registrar registrar : Registrar.loadAllCached()) {
|
||||
if (DateTimeUtils.isAtOrAfter(registrar.getLastUpdateTime(), lastUpdateTime)) {
|
||||
return true;
|
||||
|
||||
@@ -112,11 +112,11 @@ public class SyncRegistrarsSheetAction implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
final Optional<String> sheetId = Optional.ofNullable(idParam.orElse(idConfig.orElse(null)));
|
||||
if (!sheetId.isPresent()) {
|
||||
if (sheetId.isEmpty()) {
|
||||
Result.MISSINGNO.send(response, null);
|
||||
return;
|
||||
}
|
||||
if (!idParam.isPresent()) {
|
||||
if (idParam.isEmpty()) {
|
||||
if (!syncRegistrarsSheet.wereRegistrarsModified()) {
|
||||
Result.NOTMODIFIED.send(response, null);
|
||||
return;
|
||||
|
||||
@@ -22,7 +22,6 @@ import static google.registry.flows.FlowUtils.unmarshalEpp;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.flows.FlowModule.EppExceptionInProviderException;
|
||||
@@ -45,7 +44,7 @@ import org.json.simple.JSONValue;
|
||||
public final class EppController {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final String LOG_SEPARATOR = Strings.repeat("=", 40);
|
||||
private static final String LOG_SEPARATOR = "=".repeat(40);
|
||||
|
||||
@Inject FlowComponent.Builder flowComponentBuilder;
|
||||
@Inject EppMetric.Builder eppMetricBuilder;
|
||||
|
||||
@@ -14,25 +14,21 @@
|
||||
|
||||
package google.registry.flows;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import com.google.auto.value.AutoBuilder;
|
||||
|
||||
/** Object to hold metadata specific to a particular execution of a flow. */
|
||||
@AutoValue
|
||||
public abstract class FlowMetadata extends ImmutableObject {
|
||||
|
||||
public abstract boolean isSuperuser();
|
||||
public record FlowMetadata(boolean isSuperuser) {
|
||||
|
||||
public static Builder newBuilder() {
|
||||
return new AutoValue_FlowMetadata.Builder();
|
||||
return new AutoBuilder_FlowMetadata_Builder();
|
||||
}
|
||||
|
||||
/** Builder for {@link FlowMetadata} */
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
@AutoBuilder
|
||||
public interface Builder {
|
||||
|
||||
public abstract Builder setSuperuser(boolean isSuperuser);
|
||||
Builder setIsSuperuser(boolean isSuperuser);
|
||||
|
||||
public abstract FlowMetadata build();
|
||||
FlowMetadata build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ public class FlowModule {
|
||||
|
||||
@Provides
|
||||
static FlowMetadata provideFlowMetadata(@Superuser boolean isSuperuser) {
|
||||
return FlowMetadata.newBuilder().setSuperuser(isSuperuser).build();
|
||||
return FlowMetadata.newBuilder().setIsSuperuser(isSuperuser).build();
|
||||
}
|
||||
|
||||
/** Wrapper class to carry an {@link EppException} to the calling code. */
|
||||
|
||||
@@ -17,7 +17,6 @@ package google.registry.flows;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.xml.XmlTransformer.prettyPrint;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.flows.FlowModule.DryRun;
|
||||
import google.registry.flows.FlowModule.InputXml;
|
||||
@@ -36,7 +35,7 @@ import javax.inject.Provider;
|
||||
/** Run a flow, either transactionally or not, with logging and retrying as needed. */
|
||||
public class FlowRunner {
|
||||
|
||||
private static final String COMMAND_LOG_FORMAT = "EPP Command" + Strings.repeat("\n\t%s", 8);
|
||||
private static final String COMMAND_LOG_FORMAT = "EPP Command" + "\n\t%s".repeat(8);
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@@ -81,6 +80,7 @@ public class FlowRunner {
|
||||
// TODO(mcilwain/weiminyu): Use transactReadOnly() here for TransactionalFlow and transact()
|
||||
// for MutatingFlow.
|
||||
return tm().transact(
|
||||
isolationLevelOverride.orElse(null),
|
||||
() -> {
|
||||
try {
|
||||
EppOutput output = EppOutput.create(flowProvider.get().run());
|
||||
@@ -96,8 +96,7 @@ public class FlowRunner {
|
||||
} catch (EppException e) {
|
||||
throw new EppRuntimeException(e);
|
||||
}
|
||||
},
|
||||
isolationLevelOverride.orElse(null));
|
||||
});
|
||||
} catch (DryRunException e) {
|
||||
return e.output;
|
||||
} catch (EppRuntimeException e) {
|
||||
|
||||
@@ -122,7 +122,7 @@ public final class ResourceFlowUtils {
|
||||
/** Check that the given AuthInfo is present for a resource being transferred. */
|
||||
public static void verifyAuthInfoPresentForResourceTransfer(Optional<AuthInfo> authInfo)
|
||||
throws EppException {
|
||||
if (!authInfo.isPresent()) {
|
||||
if (authInfo.isEmpty()) {
|
||||
throw new MissingTransferRequestAuthInfoException();
|
||||
}
|
||||
}
|
||||
@@ -160,7 +160,7 @@ public final class ResourceFlowUtils {
|
||||
domain.getReferencedContacts().stream()
|
||||
.filter(key -> key.getKey().equals(authRepoId))
|
||||
.findFirst();
|
||||
if (!foundContact.isPresent()) {
|
||||
if (foundContact.isEmpty()) {
|
||||
throw new BadAuthInfoForResourceException();
|
||||
}
|
||||
// Check the authInfo against the contact.
|
||||
|
||||
@@ -105,7 +105,7 @@ public class TlsCredentials implements TransportCredentials {
|
||||
}
|
||||
// In the rare unexpected case that the client inet address wasn't passed along at all, then
|
||||
// by default deny access.
|
||||
if (!clientInetAddr.isPresent()) {
|
||||
if (clientInetAddr.isEmpty()) {
|
||||
logger.atWarning().log(
|
||||
"Authentication error: Missing IP address for registrar %s.", registrar.getRegistrarId());
|
||||
throw new BadRegistrarIpAddressException(clientInetAddr);
|
||||
@@ -129,8 +129,8 @@ public class TlsCredentials implements TransportCredentials {
|
||||
|
||||
@VisibleForTesting
|
||||
void validateCertificateHash(Registrar registrar) throws AuthenticationErrorException {
|
||||
if (!registrar.getClientCertificateHash().isPresent()
|
||||
&& !registrar.getFailoverClientCertificateHash().isPresent()) {
|
||||
if (registrar.getClientCertificateHash().isEmpty()
|
||||
&& registrar.getFailoverClientCertificateHash().isEmpty()) {
|
||||
if (requireSslCertificates) {
|
||||
throw new RegistrarCertificateNotConfiguredException();
|
||||
} else {
|
||||
@@ -140,7 +140,7 @@ public class TlsCredentials implements TransportCredentials {
|
||||
}
|
||||
}
|
||||
// Check that the request included the certificate hash
|
||||
if (!clientCertificateHash.isPresent()) {
|
||||
if (clientCertificateHash.isEmpty()) {
|
||||
logger.atInfo().log(
|
||||
"Request from registrar %s did not include X-SSL-Certificate.",
|
||||
registrar.getRegistrarId());
|
||||
|
||||
@@ -75,7 +75,7 @@ public final class ContactTransferQueryFlow implements TransactionalFlow {
|
||||
}
|
||||
// Note that the authorization info on the command (if present) has already been verified. If
|
||||
// it's present, then the other checks are unnecessary.
|
||||
if (!authInfo.isPresent()
|
||||
if (authInfo.isEmpty()
|
||||
&& !registrarId.equals(contact.getTransferData().getGainingRegistrarId())
|
||||
&& !registrarId.equals(contact.getTransferData().getLosingRegistrarId())) {
|
||||
throw new NotAuthorizedToViewTransferException();
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.flows.custom;
|
||||
|
||||
import com.google.auto.value.AutoBuilder;
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.flows.EppException;
|
||||
@@ -81,30 +82,23 @@ public class DomainRenewFlowCustomLogic extends BaseFlowCustomLogic {
|
||||
}
|
||||
|
||||
/** A class to encapsulate parameters for a call to {@link #afterValidation}. */
|
||||
@AutoValue
|
||||
public abstract static class AfterValidationParameters extends ImmutableObject {
|
||||
|
||||
public abstract Domain existingDomain();
|
||||
|
||||
public abstract int years();
|
||||
|
||||
public abstract DateTime now();
|
||||
public record AfterValidationParameters(Domain existingDomain, int years, DateTime now) {
|
||||
|
||||
public static Builder newBuilder() {
|
||||
return new AutoValue_DomainRenewFlowCustomLogic_AfterValidationParameters.Builder();
|
||||
return new AutoBuilder_DomainRenewFlowCustomLogic_AfterValidationParameters_Builder();
|
||||
}
|
||||
|
||||
/** Builder for {@link AfterValidationParameters}. */
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
@AutoBuilder
|
||||
public interface Builder {
|
||||
|
||||
public abstract Builder setExistingDomain(Domain existingDomain);
|
||||
Builder setExistingDomain(Domain existingDomain);
|
||||
|
||||
public abstract Builder setYears(int years);
|
||||
Builder setYears(int years);
|
||||
|
||||
public abstract Builder setNow(DateTime now);
|
||||
Builder setNow(DateTime now);
|
||||
|
||||
public abstract AfterValidationParameters build();
|
||||
AfterValidationParameters build();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,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.isRegisterBsaCreate;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.isReserved;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.isValidReservedCreate;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
|
||||
@@ -218,7 +219,7 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||
domainCheckResults,
|
||||
tldStates,
|
||||
allocationToken);
|
||||
boolean isAvailable = !message.isPresent();
|
||||
boolean isAvailable = message.isEmpty();
|
||||
checksBuilder.add(DomainCheck.create(isAvailable, domainName, message.orElse(null)));
|
||||
if (isAvailable) {
|
||||
availableDomains.add(domainName);
|
||||
@@ -269,13 +270,13 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||
if (tokenResult.isPresent()) {
|
||||
return tokenResult;
|
||||
}
|
||||
if (bsaBlockedDomains.contains(domainName)) {
|
||||
// TODO(weiminyu): extract to a constant for here and CheckApiAction.
|
||||
// Excerpt from BSA's custom message. Max len 32 chars by EPP XML schema.
|
||||
return Optional.of("Blocked by a GlobalBlock service");
|
||||
} else {
|
||||
if (isRegisterBsaCreate(domainName, allocationToken)
|
||||
|| !bsaBlockedDomains.contains(domainName)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
// TODO(weiminyu): extract to a constant for here and CheckApiAction.
|
||||
// Excerpt from BSA's custom message. Max len 32 chars by EPP XML schema.
|
||||
return Optional.of("Blocked by a GlobalBlock service");
|
||||
}
|
||||
|
||||
/** Handle the fee check extension. */
|
||||
@@ -288,7 +289,7 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||
throws EppException {
|
||||
Optional<FeeCheckCommandExtension> feeCheckOpt =
|
||||
eppInput.getSingleExtension(FeeCheckCommandExtension.class);
|
||||
if (!feeCheckOpt.isPresent()) {
|
||||
if (feeCheckOpt.isEmpty()) {
|
||||
return ImmutableList.of(); // No fee checks were requested.
|
||||
}
|
||||
FeeCheckCommandExtension<?, ?> feeCheck = feeCheckOpt.get();
|
||||
|
||||
@@ -276,7 +276,7 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
now,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
boolean defaultTokenUsed = false;
|
||||
if (!allocationToken.isPresent()) {
|
||||
if (allocationToken.isEmpty()) {
|
||||
allocationToken =
|
||||
DomainFlowUtils.checkForDefaultToken(
|
||||
tld, command.getDomainName(), CommandName.CREATE, registrarId, now);
|
||||
@@ -330,7 +330,7 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
.verifySignedMarks(launchCreate.get().getSignedMarks(), domainLabel, now)
|
||||
.getId();
|
||||
}
|
||||
verifyNotBlockedByBsa(domainLabel, tld, now);
|
||||
verifyNotBlockedByBsa(domainName, tld, now, allocationToken);
|
||||
flowCustomLogic.afterValidation(
|
||||
DomainCreateFlowCustomLogic.AfterValidationParameters.newBuilder()
|
||||
.setDomainName(domainName)
|
||||
@@ -421,8 +421,7 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
createNameCollisionOneTimePollMessage(targetId, domainHistory, registrarId, now));
|
||||
}
|
||||
entitiesToSave.add(domain, domainHistory);
|
||||
if (allocationToken.isPresent()
|
||||
&& TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) {
|
||||
if (allocationToken.isPresent() && allocationToken.get().getTokenType().isOneTimeUse()) {
|
||||
entitiesToSave.add(
|
||||
allocationTokenFlowUtils.redeemToken(
|
||||
allocationToken.get(), domainHistory.getHistoryEntryId()));
|
||||
@@ -520,7 +519,7 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
if (behavior.equals(RegistrationBehavior.BYPASS_TLD_STATE)
|
||||
|| behavior.equals(RegistrationBehavior.ANCHOR_TENANT)) {
|
||||
// Non-trademarked names with the state check bypassed are always available
|
||||
if (!claimsList.getClaimKey(domainLabel).isPresent()) {
|
||||
if (claimsList.getClaimKey(domainLabel).isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!currentState.equals(START_DATE_SUNRISE)) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import static com.google.common.collect.Sets.intersection;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.bsa.persistence.BsaLabelUtils.isLabelBlocked;
|
||||
import static google.registry.model.domain.Domain.MAX_REGISTRATION_YEARS;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.REGISTER_BSA;
|
||||
import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
|
||||
import static google.registry.model.tld.Tld.TldState.PREDELEGATION;
|
||||
import static google.registry.model.tld.Tld.TldState.QUIET_PERIOD;
|
||||
@@ -214,7 +215,7 @@ public class DomainFlowUtils {
|
||||
throw new DomainNameExistsAsTldException();
|
||||
}
|
||||
Optional<InternetDomainName> tldParsed = findTldForName(domainName);
|
||||
if (!tldParsed.isPresent()) {
|
||||
if (tldParsed.isEmpty()) {
|
||||
throw new TldDoesNotExistException(domainName.parent().toString());
|
||||
}
|
||||
if (domainName.parts().size() != tldParsed.get().parts().size() + 1) {
|
||||
@@ -255,7 +256,7 @@ public class DomainFlowUtils {
|
||||
Optional<String> idnTableName =
|
||||
IDN_LABEL_VALIDATOR.findValidIdnTableForTld(
|
||||
domainName.parts().get(0), domainName.parent().toString());
|
||||
if (!idnTableName.isPresent()) {
|
||||
if (idnTableName.isEmpty()) {
|
||||
throw new InvalidIdnDomainLabelException();
|
||||
}
|
||||
return idnTableName.get();
|
||||
@@ -265,9 +266,14 @@ public class DomainFlowUtils {
|
||||
* Verifies that the {@code domainLabel} is not blocked by any BSA block label for the given
|
||||
* {@code tld} at the specified time.
|
||||
*/
|
||||
public static void verifyNotBlockedByBsa(String domainLabel, Tld tld, DateTime now)
|
||||
public static void verifyNotBlockedByBsa(
|
||||
InternetDomainName domainName,
|
||||
Tld tld,
|
||||
DateTime now,
|
||||
Optional<AllocationToken> allocationToken)
|
||||
throws DomainLabelBlockedByBsaException {
|
||||
if (isBlockedByBsa(domainLabel, tld, now)) {
|
||||
if (!isRegisterBsaCreate(domainName, allocationToken)
|
||||
&& isBlockedByBsa(domainName.parts().get(0), tld, now)) {
|
||||
throw new DomainLabelBlockedByBsaException();
|
||||
}
|
||||
}
|
||||
@@ -311,6 +317,15 @@ public class DomainFlowUtils {
|
||||
&& token.get().getDomainName().get().equals(domainName.toString());
|
||||
}
|
||||
|
||||
/** Returns whether a given domain create request may bypass the BSA block check. */
|
||||
public static boolean isRegisterBsaCreate(
|
||||
InternetDomainName domainName, Optional<AllocationToken> token) {
|
||||
return token.isPresent()
|
||||
&& token.get().getTokenType().equals(REGISTER_BSA)
|
||||
&& token.get().getDomainName().isPresent()
|
||||
&& token.get().getDomainName().get().equals(domainName.toString());
|
||||
}
|
||||
|
||||
/** Check if the registrar running the flow has access to the TLD in question. */
|
||||
public static void checkAllowedAccessToTld(String registrarId, String tld) throws EppException {
|
||||
if (!Registrar.loadByRegistrarIdCached(registrarId).get().getAllowedTlds().contains(tld)) {
|
||||
@@ -353,7 +368,7 @@ public class DomainFlowUtils {
|
||||
}
|
||||
ImmutableList<DomainDsData> invalidDigestTypes =
|
||||
dsData.stream()
|
||||
.filter(ds -> !DigestType.fromWireValue(ds.getDigestType()).isPresent())
|
||||
.filter(ds -> DigestType.fromWireValue(ds.getDigestType()).isEmpty())
|
||||
.collect(toImmutableList());
|
||||
if (!invalidDigestTypes.isEmpty()) {
|
||||
throw new InvalidDsRecordException(
|
||||
@@ -801,7 +816,7 @@ public class DomainFlowUtils {
|
||||
FeesAndCredits feesAndCredits,
|
||||
boolean defaultTokenUsed)
|
||||
throws EppException {
|
||||
if (feesAndCredits.hasAnyPremiumFees() && !feeCommand.isPresent()) {
|
||||
if (feesAndCredits.hasAnyPremiumFees() && feeCommand.isEmpty()) {
|
||||
throw new FeesRequiredForPremiumNameException();
|
||||
}
|
||||
validateFeesAckedIfPresent(feeCommand, feesAndCredits, defaultTokenUsed);
|
||||
@@ -822,7 +837,7 @@ public class DomainFlowUtils {
|
||||
// Check for the case where a fee command extension was required but not provided.
|
||||
// This only happens when the total fees are non-zero and include custom fees requiring the
|
||||
// extension.
|
||||
if (!feeCommand.isPresent()) {
|
||||
if (feeCommand.isEmpty()) {
|
||||
if (!feesAndCredits.getEapCost().isZero()) {
|
||||
throw new FeesRequiredDuringEarlyAccessProgramException(feesAndCredits.getEapCost());
|
||||
}
|
||||
@@ -1034,7 +1049,7 @@ public class DomainFlowUtils {
|
||||
/** Validate the secDNS extension, if present. */
|
||||
static Optional<SecDnsCreateExtension> validateSecDnsExtension(
|
||||
Optional<SecDnsCreateExtension> secDnsCreate) throws EppException {
|
||||
if (!secDnsCreate.isPresent()) {
|
||||
if (secDnsCreate.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
if (secDnsCreate.get().getDsData() == null) {
|
||||
|
||||
@@ -73,7 +73,6 @@ import google.registry.model.domain.fee.FeeTransformResponseExtension;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.domain.token.AllocationTokenExtension;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
@@ -183,7 +182,7 @@ public final class DomainRenewFlow implements MutatingFlow {
|
||||
CommandName.RENEW,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
boolean defaultTokenUsed = false;
|
||||
if (!allocationToken.isPresent()) {
|
||||
if (allocationToken.isEmpty()) {
|
||||
allocationToken =
|
||||
DomainFlowUtils.checkForDefaultToken(
|
||||
tld, existingDomain.getDomainName(), CommandName.RENEW, registrarId, now);
|
||||
@@ -258,8 +257,7 @@ public final class DomainRenewFlow implements MutatingFlow {
|
||||
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||
entitiesToSave.add(
|
||||
newDomain, domainHistory, explicitRenewEvent, newAutorenewEvent, newAutorenewPollMessage);
|
||||
if (allocationToken.isPresent()
|
||||
&& TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) {
|
||||
if (allocationToken.isPresent() && allocationToken.get().getTokenType().isOneTimeUse()) {
|
||||
entitiesToSave.add(
|
||||
allocationTokenFlowUtils.redeemToken(
|
||||
allocationToken.get(), domainHistory.getHistoryEntryId()));
|
||||
|
||||
@@ -81,7 +81,7 @@ public final class DomainTransferQueryFlow implements TransactionalFlow {
|
||||
}
|
||||
// Note that the authorization info on the command (if present) has already been verified. If
|
||||
// it's present, then the other checks are unnecessary.
|
||||
if (!authInfo.isPresent()
|
||||
if (authInfo.isEmpty()
|
||||
&& !registrarId.equals(transferData.getGainingRegistrarId())
|
||||
&& !registrarId.equals(transferData.getLosingRegistrarId())) {
|
||||
throw new NotAuthorizedToViewTransferException();
|
||||
|
||||
@@ -201,7 +201,7 @@ public final class DomainTransferRequestFlow implements MutatingFlow {
|
||||
Optional<FeesAndCredits> feesAndCredits;
|
||||
if (period.getValue() == 0) {
|
||||
feesAndCredits = Optional.empty();
|
||||
} else if (!existingDomain.getCurrentBulkToken().isPresent()) {
|
||||
} else if (existingDomain.getCurrentBulkToken().isEmpty()) {
|
||||
feesAndCredits =
|
||||
Optional.of(pricingLogic.getTransferPrice(tld, targetId, now, existingBillingRecurrence));
|
||||
} else {
|
||||
|
||||
@@ -36,7 +36,6 @@ import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.domain.token.AllocationTokenExtension;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.model.tld.Tld;
|
||||
@@ -105,8 +104,7 @@ public class AllocationTokenFlowUtils {
|
||||
/** Redeems a SINGLE_USE {@link AllocationToken}, returning the redeemed copy. */
|
||||
public AllocationToken redeemToken(AllocationToken token, HistoryEntryId redemptionHistoryId) {
|
||||
checkArgument(
|
||||
TokenType.SINGLE_USE.equals(token.getTokenType()),
|
||||
"Only SINGLE_USE tokens can be marked as redeemed");
|
||||
token.getTokenType().isOneTimeUse(), "Only SINGLE_USE tokens can be marked as redeemed");
|
||||
return token.asBuilder().setRedemptionHistoryId(redemptionHistoryId).build();
|
||||
}
|
||||
|
||||
@@ -184,7 +182,7 @@ public class AllocationTokenFlowUtils {
|
||||
maybeTokenEntity =
|
||||
tm().transact(() -> tm().loadByKeyIfPresent(VKey.create(AllocationToken.class, token)));
|
||||
|
||||
if (!maybeTokenEntity.isPresent()) {
|
||||
if (maybeTokenEntity.isEmpty()) {
|
||||
throw new InvalidAllocationTokenException();
|
||||
}
|
||||
if (maybeTokenEntity.get().isRedeemed()) {
|
||||
@@ -201,7 +199,7 @@ public class AllocationTokenFlowUtils {
|
||||
DateTime now,
|
||||
Optional<AllocationTokenExtension> extension)
|
||||
throws EppException {
|
||||
if (!extension.isPresent()) {
|
||||
if (extension.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
AllocationToken tokenEntity = loadToken(extension.get().getAllocationToken());
|
||||
@@ -224,7 +222,7 @@ public class AllocationTokenFlowUtils {
|
||||
CommandName commandName,
|
||||
Optional<AllocationTokenExtension> extension)
|
||||
throws EppException {
|
||||
if (!extension.isPresent()) {
|
||||
if (extension.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
AllocationToken tokenEntity = loadToken(extension.get().getAllocationToken());
|
||||
@@ -256,7 +254,7 @@ public class AllocationTokenFlowUtils {
|
||||
|
||||
public static Domain maybeApplyBulkPricingRemovalToken(
|
||||
Domain domain, Optional<AllocationToken> allocationToken) {
|
||||
if (!allocationToken.isPresent()
|
||||
if (allocationToken.isEmpty()
|
||||
|| !TokenBehavior.REMOVE_BULK_PRICING.equals(allocationToken.get().getTokenBehavior())) {
|
||||
return domain;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ public class HostFlowUtils {
|
||||
public static Optional<Domain> lookupSuperordinateDomain(
|
||||
InternetDomainName hostName, DateTime now) throws EppException {
|
||||
Optional<InternetDomainName> tld = findTldForName(hostName);
|
||||
if (!tld.isPresent()) {
|
||||
if (tld.isEmpty()) {
|
||||
// This is an host on a TLD we don't run, therefore obviously external, so we are done.
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -91,7 +91,7 @@ public class HostFlowUtils {
|
||||
.skip(hostName.parts().size() - (tld.get().parts().size() + 1))
|
||||
.collect(joining("."));
|
||||
Optional<Domain> superordinateDomain = loadByForeignKey(Domain.class, domainName, now);
|
||||
if (!superordinateDomain.isPresent() || !isActive(superordinateDomain.get(), now)) {
|
||||
if (superordinateDomain.isEmpty() || !isActive(superordinateDomain.get(), now)) {
|
||||
throw new SuperordinateDomainDoesNotExistException(domainName);
|
||||
}
|
||||
return superordinateDomain;
|
||||
|
||||
@@ -148,23 +148,25 @@ public class FlowPicker {
|
||||
* <p>This provider must be tried before {@link #RESOURCE_CRUD_FLOW_PROVIDER}. Otherwise, the
|
||||
* regular domain update flow will match first.
|
||||
*/
|
||||
private static final FlowProvider DOMAIN_RESTORE_FLOW_PROVIDER = new FlowProvider() {
|
||||
@Override
|
||||
Class<? extends Flow> get(
|
||||
EppInput eppInput, InnerCommand innerCommand, ResourceCommand resourceCommand) {
|
||||
if (!(resourceCommand instanceof DomainCommand.Update)) {
|
||||
return null;
|
||||
}
|
||||
Optional<RgpUpdateExtension> rgpUpdateExtension =
|
||||
eppInput.getSingleExtension(RgpUpdateExtension.class);
|
||||
if (!rgpUpdateExtension.isPresent()) {
|
||||
return null;
|
||||
}
|
||||
// Restore command with an op of "report" is not currently supported.
|
||||
return (rgpUpdateExtension.get().getRestoreCommand().getRestoreOp() == RestoreOp.REQUEST)
|
||||
? DomainRestoreRequestFlow.class
|
||||
: UnimplementedRestoreFlow.class;
|
||||
}};
|
||||
private static final FlowProvider DOMAIN_RESTORE_FLOW_PROVIDER =
|
||||
new FlowProvider() {
|
||||
@Override
|
||||
Class<? extends Flow> get(
|
||||
EppInput eppInput, InnerCommand innerCommand, ResourceCommand resourceCommand) {
|
||||
if (!(resourceCommand instanceof DomainCommand.Update)) {
|
||||
return null;
|
||||
}
|
||||
Optional<RgpUpdateExtension> rgpUpdateExtension =
|
||||
eppInput.getSingleExtension(RgpUpdateExtension.class);
|
||||
if (rgpUpdateExtension.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
// Restore command with an op of "report" is not currently supported.
|
||||
return (rgpUpdateExtension.get().getRestoreCommand().getRestoreOp() == RestoreOp.REQUEST)
|
||||
? DomainRestoreRequestFlow.class
|
||||
: UnimplementedRestoreFlow.class;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The claims check flow is keyed on the type of the {@link ResourceCommand} and on having the
|
||||
@@ -180,7 +182,7 @@ public class FlowPicker {
|
||||
}
|
||||
Optional<LaunchCheckExtension> launchCheck =
|
||||
eppInput.getSingleExtension(LaunchCheckExtension.class);
|
||||
if (!launchCheck.isPresent()
|
||||
if (launchCheck.isEmpty()
|
||||
|| CheckType.AVAILABILITY.equals(launchCheck.get().getCheckType())) {
|
||||
// We don't distinguish between registry phases for "avail", so don't bother checking
|
||||
// phase.
|
||||
|
||||
@@ -86,7 +86,7 @@ public final class PollAckFlow implements MutatingFlow {
|
||||
// it as if it doesn't exist yet. Same for if the message ID year isn't the same as the actual
|
||||
// poll message's event time (that means they're passing in an old already-acked ID).
|
||||
Optional<PollMessage> maybePollMessage = tm().loadByKeyIfPresent(pollMessageKey);
|
||||
if (!maybePollMessage.isPresent()
|
||||
if (maybePollMessage.isEmpty()
|
||||
|| !isBeforeOrAt(maybePollMessage.get().getEventTime(), now)
|
||||
|| !makePollMessageExternalId(maybePollMessage.get()).equals(messageId)) {
|
||||
throw new MessageDoesNotExistException(messageId);
|
||||
|
||||
@@ -66,7 +66,7 @@ public final class PollRequestFlow implements TransactionalFlow {
|
||||
// Return the oldest message from the queue.
|
||||
DateTime now = tm().getTransactionTime();
|
||||
Optional<PollMessage> maybePollMessage = getFirstPollMessage(registrarId, now);
|
||||
if (!maybePollMessage.isPresent()) {
|
||||
if (maybePollMessage.isEmpty()) {
|
||||
return responseBuilder.setResultFromCode(SUCCESS_WITH_NO_MESSAGES).build();
|
||||
}
|
||||
PollMessage pollMessage = maybePollMessage.get();
|
||||
|
||||
@@ -123,7 +123,7 @@ public class LoginFlow implements MutatingFlow {
|
||||
serviceExtensionUrisBuilder.add(uri);
|
||||
}
|
||||
Optional<Registrar> registrar = Registrar.loadByRegistrarIdCached(login.getClientId());
|
||||
if (!registrar.isPresent()) {
|
||||
if (registrar.isEmpty()) {
|
||||
throw new BadRegistrarIdException(login.getClientId());
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ public class LoginFlow implements MutatingFlow {
|
||||
});
|
||||
// Load fresh from database (bypassing the cache) to ensure we don't save stale data.
|
||||
Optional<Registrar> freshRegistrar = Registrar.loadByRegistrarId(login.getClientId());
|
||||
if (!freshRegistrar.isPresent()) {
|
||||
if (freshRegistrar.isEmpty()) {
|
||||
throw new BadRegistrarIdException(login.getClientId());
|
||||
}
|
||||
tm().put(freshRegistrar.get().asBuilder().setPassword(newPassword).build());
|
||||
|
||||
@@ -118,6 +118,14 @@ public class GcsUtils implements Serializable {
|
||||
storage().delete(blobId);
|
||||
}
|
||||
|
||||
/** Update file content type on existing GCS file */
|
||||
public void updateContentType(BlobId blobId, String contentType) throws StorageException {
|
||||
if (existsAndNotEmpty(blobId)) {
|
||||
Blob blob = storage().get(blobId);
|
||||
blob.toBuilder().setContentType(contentType).build().update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all object names within a bucket for a given prefix.
|
||||
*
|
||||
|
||||
@@ -539,7 +539,7 @@ public class DomainBase extends EppResource
|
||||
for (GracePeriod gracePeriod : almostBuilt.getGracePeriods()) {
|
||||
if (isBeforeOrAt(gracePeriod.getExpirationTime(), now)) {
|
||||
builder.removeGracePeriod(gracePeriod);
|
||||
if (!newLastEppUpdateTime.isPresent()
|
||||
if (newLastEppUpdateTime.isEmpty()
|
||||
|| isBeforeOrAt(newLastEppUpdateTime.get(), gracePeriod.getExpirationTime())) {
|
||||
newLastEppUpdateTime = Optional.of(gracePeriod.getExpirationTime());
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ public final class RegistryLock extends UpdateAutoTimestampEntity implements Bui
|
||||
|
||||
/** Returns true iff the lock was requested >= 1 hour ago and has not been verified. */
|
||||
public boolean isLockRequestExpired(DateTime now) {
|
||||
return !getLockCompletionTime().isPresent()
|
||||
return getLockCompletionTime().isEmpty()
|
||||
&& isBeforeOrAt(getLockRequestTime(), now.minusHours(1));
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ public final class RegistryLock extends UpdateAutoTimestampEntity implements Bui
|
||||
public boolean isUnlockRequestExpired(DateTime now) {
|
||||
Optional<DateTime> unlockRequestTimestamp = getUnlockRequestTime();
|
||||
return unlockRequestTimestamp.isPresent()
|
||||
&& !getUnlockCompletionTime().isPresent()
|
||||
&& getUnlockCompletionTime().isEmpty()
|
||||
&& isBeforeOrAt(unlockRequestTimestamp.get(), now.minusHours(1));
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import static google.registry.model.domain.token.AllocationToken.TokenStatus.CAN
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.ENDED;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.NOT_STARTED;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.VALID;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.REGISTER_BSA;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.CollectionUtils.forceEmptyToNull;
|
||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
||||
@@ -120,18 +121,37 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
/** Type of the token that indicates how and where it should be used. */
|
||||
public enum TokenType {
|
||||
/** Token used for bulk pricing */
|
||||
BULK_PRICING,
|
||||
BULK_PRICING(/* isOneTimeUse= */ false),
|
||||
/** Token saved on a TLD to use if no other token is passed from the client */
|
||||
DEFAULT_PROMO,
|
||||
DEFAULT_PROMO(/* isOneTimeUse= */ false),
|
||||
/** This is the old name for what is now BULK_PRICING. */
|
||||
// TODO(sarahbot@): Remove this type once all tokens of this type have been scrubbed from the
|
||||
// database
|
||||
@Deprecated
|
||||
PACKAGE,
|
||||
PACKAGE(/* isOneTimeUse= */ false),
|
||||
/** Invalid after use */
|
||||
SINGLE_USE,
|
||||
SINGLE_USE(/* isOneTimeUse= */ true),
|
||||
/** Do not expire after use */
|
||||
UNLIMITED_USE,
|
||||
UNLIMITED_USE(/* isOneTimeUse= */ false),
|
||||
/**
|
||||
* Allows bypassing the BSA check during domain creation, otherwise has the same semantics as
|
||||
* {@link #SINGLE_USE}.
|
||||
*
|
||||
* <p>This token applies to a single domain only. If the domain is not blocked by BSA at the
|
||||
* redemption time this token is processed like {@code SINGLE_USE}, as mentioned above.
|
||||
*/
|
||||
REGISTER_BSA(/* isOneTimeUse= */ true);
|
||||
|
||||
private final boolean isOneTimeUse;
|
||||
|
||||
private TokenType(boolean isOneTimeUse) {
|
||||
this.isOneTimeUse = isOneTimeUse;
|
||||
}
|
||||
|
||||
/** Returns true if token should be invalidated after use. */
|
||||
public boolean isOneTimeUse() {
|
||||
return this.isOneTimeUse;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -361,12 +381,11 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
|| !getInstance().discountPremiums,
|
||||
"Bulk tokens cannot discount premium names");
|
||||
checkArgument(
|
||||
getInstance().domainName == null || TokenType.SINGLE_USE.equals(getInstance().tokenType),
|
||||
"Domain name can only be specified for SINGLE_USE tokens");
|
||||
getInstance().domainName == null || getInstance().tokenType.isOneTimeUse(),
|
||||
"Domain name can only be specified for SINGLE_USE or REGISTER_BSA tokens");
|
||||
checkArgument(
|
||||
getInstance().redemptionHistoryId == null
|
||||
|| TokenType.SINGLE_USE.equals(getInstance().tokenType),
|
||||
"Redemption history entry can only be specified for SINGLE_USE tokens");
|
||||
getInstance().redemptionHistoryId == null || getInstance().tokenType.isOneTimeUse(),
|
||||
"Redemption history entry can only be specified for SINGLE_USE or REGISTER_BSA tokens");
|
||||
checkArgument(
|
||||
getInstance().tokenType != TokenType.BULK_PRICING
|
||||
|| (getInstance().allowedClientIds != null
|
||||
@@ -378,6 +397,10 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
checkArgument(
|
||||
getInstance().discountFraction > 0 || getInstance().discountYears == 1,
|
||||
"Discount years can only be specified along with a discount fraction");
|
||||
if (getInstance().getTokenType().equals(REGISTER_BSA)) {
|
||||
checkArgumentNotNull(
|
||||
getInstance().domainName, "REGISTER_BSA tokens must be tied to a domain");
|
||||
}
|
||||
if (getInstance().registrationBehavior.equals(RegistrationBehavior.ANCHOR_TENANT)) {
|
||||
checkArgumentNotNull(
|
||||
getInstance().domainName, "ANCHOR_TENANT tokens must be tied to a domain");
|
||||
|
||||
@@ -19,7 +19,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import static google.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
@@ -129,26 +128,11 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
return String.format("%s-%s", scope, resourceName);
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
abstract static class AcquireResult {
|
||||
public abstract DateTime transactionTime();
|
||||
|
||||
@Nullable
|
||||
public abstract Lock existingLock();
|
||||
|
||||
@Nullable
|
||||
public abstract Lock newLock();
|
||||
|
||||
public abstract LockState lockState();
|
||||
|
||||
public static AcquireResult create(
|
||||
DateTime transactionTime,
|
||||
@Nullable Lock existingLock,
|
||||
@Nullable Lock newLock,
|
||||
LockState lockState) {
|
||||
return new AutoValue_Lock_AcquireResult(transactionTime, existingLock, newLock, lockState);
|
||||
}
|
||||
}
|
||||
record AcquireResult(
|
||||
DateTime transactionTime,
|
||||
@Nullable Lock existingLock,
|
||||
@Nullable Lock newLock,
|
||||
LockState lockState) {}
|
||||
|
||||
private static void logAcquireResult(AcquireResult acquireResult) {
|
||||
try {
|
||||
@@ -204,13 +188,13 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
lockState = LockState.TIMED_OUT;
|
||||
} else {
|
||||
lockState = LockState.IN_USE;
|
||||
return AcquireResult.create(now, lock, null, lockState);
|
||||
return new AcquireResult(now, lock, null, lockState);
|
||||
}
|
||||
|
||||
Lock newLock = create(resourceName, scope, now, leaseLength);
|
||||
tm().put(newLock);
|
||||
|
||||
return AcquireResult.create(now, lock, newLock, lockState);
|
||||
return new AcquireResult(now, lock, newLock, lockState);
|
||||
};
|
||||
AcquireResult acquireResult = tm().transact(lockAcquirer);
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ public class ServerSecret extends CrossTldSingleton {
|
||||
// Make sure we're in a transaction and attempt to load any existing secret, then
|
||||
// create it if it's absent.
|
||||
Optional<ServerSecret> secret = tm().loadSingleton(ServerSecret.class);
|
||||
if (!secret.isPresent()) {
|
||||
if (secret.isEmpty()) {
|
||||
secret = Optional.of(create(UUID.randomUUID()));
|
||||
tm().insert(secret.get());
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ import google.registry.tldconfig.idn.IdnTableEnum;
|
||||
import google.registry.util.Idn;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
@@ -458,6 +459,8 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
@JsonDeserialize(using = CurrencyDeserializer.class)
|
||||
CurrencyUnit currency = DEFAULT_CURRENCY;
|
||||
|
||||
// TODO(sarahbot@): Remove this field and make createBillingCostTransitions not-null once all TLDs
|
||||
// are populated with a create cost transition map
|
||||
/** The per-year billing cost for registering a new domain name. */
|
||||
@Type(type = JodaMoneyType.TYPE_NAME)
|
||||
@Columns(
|
||||
@@ -467,6 +470,12 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
})
|
||||
Money createBillingCost = DEFAULT_CREATE_BILLING_COST;
|
||||
|
||||
// TODO(sarahbot@): Make this field not null and add a default value once field is populated on
|
||||
// all existing TLDs
|
||||
/** A property that transitions to different create billing costs at different times. */
|
||||
@JsonDeserialize(using = TimedTransitionPropertyMoneyDeserializer.class)
|
||||
TimedTransitionProperty<Money> createBillingCostTransitions;
|
||||
|
||||
/** The one-time billing cost for restoring a domain name from the redemption grace period. */
|
||||
@Type(type = JodaMoneyType.TYPE_NAME)
|
||||
@Columns(
|
||||
@@ -676,6 +685,13 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
return createBillingCost;
|
||||
}
|
||||
|
||||
public ImmutableSortedMap<DateTime, Money> getCreateBillingCostTransitions() {
|
||||
return Objects.requireNonNullElseGet(
|
||||
createBillingCostTransitions,
|
||||
() -> TimedTransitionProperty.withInitialValue(getCreateBillingCost()))
|
||||
.toValueMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the add-on cost of a domain restore (the flat tld-wide fee charged in addition to one
|
||||
* year of renewal for that name).
|
||||
@@ -959,6 +975,17 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCreateBillingCostTransitions(
|
||||
ImmutableSortedMap<DateTime, 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;
|
||||
}
|
||||
|
||||
public Builder setReservedListsByName(Set<String> reservedListNames) {
|
||||
// TODO(b/309175133): forbid if enrolled with BSA
|
||||
checkArgument(reservedListNames != null, "reservedListNames must not be null");
|
||||
@@ -1131,6 +1158,10 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
// 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();
|
||||
// TODO(sarahbot@): Remove null check when createBillingCostTransitions field is made not-null
|
||||
if (instance.createBillingCostTransitions != null) {
|
||||
instance.createBillingCostTransitions.checkValidity();
|
||||
}
|
||||
instance.renewBillingCostTransitions.checkValidity();
|
||||
instance.eapFeeSchedule.checkValidity();
|
||||
// All costs must be in the expected currency.
|
||||
|
||||
@@ -115,7 +115,7 @@ public final class PremiumListDao {
|
||||
*/
|
||||
public static Optional<Money> getPremiumPrice(String premiumListName, String label) {
|
||||
Optional<PremiumList> maybeLoadedList = getLatestRevision(premiumListName);
|
||||
if (!maybeLoadedList.isPresent()) {
|
||||
if (maybeLoadedList.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
PremiumList loadedList = maybeLoadedList.get();
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package google.registry.module;
|
||||
|
||||
import com.google.appengine.api.LifecycleManager;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.monitoring.metrics.MetricReporter;
|
||||
import dagger.Lazy;
|
||||
@@ -46,25 +45,26 @@ public class ServletBase extends HttpServlet {
|
||||
public void init() {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
|
||||
// If metric reporter failed to instantiate for any reason (bad keyring, bad json credential,
|
||||
// etc), we log the error but keep the main thread running. Also the shutdown hook will only be
|
||||
// registered if metric reporter starts up correctly.
|
||||
// If the metric reporter failed to instantiate for any reason (bad keyring, bad json
|
||||
// credential, etc.), we log the error but keep the main thread running. Also, the shutdown hook
|
||||
// will only be registered if the metric reporter starts up correctly.
|
||||
try {
|
||||
metricReporter.get().startAsync().awaitRunning(java.time.Duration.ofSeconds(10));
|
||||
logger.atInfo().log("Started up MetricReporter.");
|
||||
LifecycleManager.getInstance()
|
||||
.setShutdownHook(
|
||||
() -> {
|
||||
try {
|
||||
metricReporter
|
||||
.get()
|
||||
.stopAsync()
|
||||
.awaitTerminated(java.time.Duration.ofSeconds(10));
|
||||
logger.atInfo().log("Shut down MetricReporter.");
|
||||
} catch (TimeoutException e) {
|
||||
logger.atSevere().withCause(e).log("Failed to stop MetricReporter.");
|
||||
}
|
||||
});
|
||||
Runtime.getRuntime()
|
||||
.addShutdownHook(
|
||||
new Thread(
|
||||
() -> {
|
||||
try {
|
||||
metricReporter
|
||||
.get()
|
||||
.stopAsync()
|
||||
.awaitTerminated(java.time.Duration.ofSeconds(10));
|
||||
logger.atInfo().log("Shut down MetricReporter.");
|
||||
} catch (TimeoutException e) {
|
||||
logger.atSevere().withCause(e).log("Failed to stop MetricReporter.");
|
||||
}
|
||||
}));
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log("Failed to initialize MetricReporter.");
|
||||
}
|
||||
|
||||
@@ -175,14 +175,14 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
public <T> T reTransact(Callable<T> work) {
|
||||
// This prevents inner transaction from retrying, thus avoiding a cascade retry effect.
|
||||
if (inTransaction()) {
|
||||
return transactNoRetry(work, null);
|
||||
return transactNoRetry(null, work);
|
||||
}
|
||||
return retrier.callWithRetry(
|
||||
() -> transactNoRetry(work, null), JpaRetries::isFailedTxnRetriable);
|
||||
() -> transactNoRetry(null, work), JpaRetries::isFailedTxnRetriable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transact(Callable<T> work, TransactionIsolationLevel isolationLevel) {
|
||||
public <T> T transact(TransactionIsolationLevel isolationLevel, Callable<T> work) {
|
||||
if (inTransaction()) {
|
||||
if (!getHibernateAllowNestedTransactions()) {
|
||||
throw new IllegalStateException(NESTED_TRANSACTION_MESSAGE);
|
||||
@@ -192,19 +192,25 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
logger.atWarning().withStackTrace(StackSize.MEDIUM).log(NESTED_TRANSACTION_MESSAGE);
|
||||
}
|
||||
// This prevents inner transaction from retrying, thus avoiding a cascade retry effect.
|
||||
return transactNoRetry(work, isolationLevel);
|
||||
return transactNoRetry(isolationLevel, work);
|
||||
}
|
||||
return retrier.callWithRetry(
|
||||
() -> transactNoRetry(work, isolationLevel), JpaRetries::isFailedTxnRetriable);
|
||||
() -> transactNoRetry(isolationLevel, work), JpaRetries::isFailedTxnRetriable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transact(Callable<T> work) {
|
||||
return transact(work, null);
|
||||
return transact(null, work);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transactNoRetry(Callable<T> work) {
|
||||
return transactNoRetry(null, work);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transactNoRetry(
|
||||
Callable<T> work, @Nullable TransactionIsolationLevel isolationLevel) {
|
||||
@Nullable TransactionIsolationLevel isolationLevel, Callable<T> work) {
|
||||
if (inTransaction()) {
|
||||
// This check will no longer be necessary when the transact() method always throws
|
||||
// inside a nested transaction, as the only way to pass a non-null isolation level
|
||||
@@ -265,18 +271,18 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transact(ThrowingRunnable work, TransactionIsolationLevel isolationLevel) {
|
||||
public void transact(TransactionIsolationLevel isolationLevel, ThrowingRunnable work) {
|
||||
transact(
|
||||
isolationLevel,
|
||||
() -> {
|
||||
work.run();
|
||||
return null;
|
||||
},
|
||||
isolationLevel);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transact(ThrowingRunnable work) {
|
||||
transact(work, null);
|
||||
transact(null, work);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -61,7 +61,31 @@ public interface TransactionManager {
|
||||
* Executes the work in a transaction at the given {@link TransactionIsolationLevel} and returns
|
||||
* the result.
|
||||
*/
|
||||
<T> T transact(Callable<T> work, TransactionIsolationLevel isolationLevel);
|
||||
<T> T transact(TransactionIsolationLevel isolationLevel, Callable<T> work);
|
||||
|
||||
/**
|
||||
* Executes the work in a transaction and returns the result, without retrying upon retryable
|
||||
* exceptions.
|
||||
*
|
||||
* <p>This method should only be used when the transaction contains side effects that are not
|
||||
* rolled back by the transaction manager, for example in {@link
|
||||
* google.registry.beam.common.RegistryJpaIO} where the results from a query are streamed to the
|
||||
* next transformation inside a transaction, as the result stream has to materialize to a list
|
||||
* outside a transaction and doing so would greatly affect the parallelism of the pipeline.
|
||||
*/
|
||||
<T> T transactNoRetry(Callable<T> work);
|
||||
|
||||
/**
|
||||
* Executes the work in a transaction at the given {@link TransactionIsolationLevel} and returns
|
||||
* the result, without retrying upon retryable exceptions.
|
||||
*
|
||||
* <p>This method should only be used when the transaction contains side effects that are not
|
||||
* rolled back by the transaction manager, for example in {@link
|
||||
* google.registry.beam.common.RegistryJpaIO} where the results from a query are streamed to the
|
||||
* next transformation inside a transaction, as the result stream has to materialize to a list
|
||||
* outside a transaction and doing so would greatly affect the parallelism of the pipeline.
|
||||
*/
|
||||
<T> T transactNoRetry(TransactionIsolationLevel isolationLevel, Callable<T> work);
|
||||
|
||||
/**
|
||||
* Executes the work in a (potentially wrapped) transaction and returns the result.
|
||||
@@ -83,7 +107,7 @@ public interface TransactionManager {
|
||||
void transact(ThrowingRunnable work);
|
||||
|
||||
/** Executes the work in a transaction at the given {@link TransactionIsolationLevel}. */
|
||||
void transact(ThrowingRunnable work, TransactionIsolationLevel isolationLevel);
|
||||
void transact(TransactionIsolationLevel isolationLevel, ThrowingRunnable work);
|
||||
|
||||
/**
|
||||
* Executes the work in a (potentially wrapped) transaction and returns the result.
|
||||
|
||||
@@ -387,7 +387,7 @@ abstract class AbstractJsonableObject implements Jsonable {
|
||||
*/
|
||||
static void verifyAllowedJsonKeyName(String name, @Nullable Member member, Class<?> clazz) {
|
||||
Optional<ImmutableSet<String>> allowedFieldNames = getNameRestriction(clazz);
|
||||
if (!allowedFieldNames.isPresent()) {
|
||||
if (allowedFieldNames.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
checkState(
|
||||
@@ -416,7 +416,7 @@ abstract class AbstractJsonableObject implements Jsonable {
|
||||
// We ignore any Optional that are empty, as if they didn't exist at all
|
||||
if (object instanceof Optional) {
|
||||
Optional<?> optional = (Optional<?>) object;
|
||||
if (!optional.isPresent()) {
|
||||
if (optional.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
object = optional.get();
|
||||
|
||||
@@ -64,7 +64,7 @@ public class RdapDomainAction extends RdapActionBase {
|
||||
Domain.class,
|
||||
pathSearchString,
|
||||
shouldIncludeDeleted() ? START_OF_TIME : rdapJsonFormatter.getRequestTime());
|
||||
if (!domain.isPresent() || !isAuthorized(domain.get())) {
|
||||
if (domain.isEmpty() || !isAuthorized(domain.get())) {
|
||||
// RFC7480 5.3 - if the server wishes to respond that it doesn't have data satisfying the
|
||||
// query, it MUST reply with 404 response code.
|
||||
//
|
||||
|
||||
@@ -342,7 +342,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
Host.class,
|
||||
partialStringQuery.getInitialString(),
|
||||
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
|
||||
return (!host.isPresent()
|
||||
return (host.isEmpty()
|
||||
|| !desiredRegistrar.get().equals(host.get().getPersistedCurrentSponsorRegistrarId()))
|
||||
? ImmutableList.of()
|
||||
: ImmutableList.of(host.get().createVKey());
|
||||
|
||||
@@ -121,7 +121,7 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
|
||||
// Check the subtype.
|
||||
Subtype subtype;
|
||||
if (!subtypeParam.isPresent() || subtypeParam.get().equalsIgnoreCase("all")) {
|
||||
if (subtypeParam.isEmpty() || subtypeParam.get().equalsIgnoreCase("all")) {
|
||||
subtype = Subtype.ALL;
|
||||
} else if (subtypeParam.get().equalsIgnoreCase("contacts")) {
|
||||
subtype = Subtype.CONTACTS;
|
||||
@@ -133,7 +133,7 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
|
||||
CursorType cursorType;
|
||||
Optional<String> cursorQueryString;
|
||||
if (!cursorString.isPresent()) {
|
||||
if (cursorString.isEmpty()) {
|
||||
cursorType = CursorType.NONE;
|
||||
cursorQueryString = Optional.empty();
|
||||
} else {
|
||||
|
||||
@@ -101,7 +101,7 @@ public final class RdapModule {
|
||||
@Provides
|
||||
static RdapAuthorization provideRdapAuthorization(
|
||||
AuthResult authResult, AuthenticatedRegistrarAccessor registrarAccessor) {
|
||||
if (!authResult.userAuthInfo().isPresent()) {
|
||||
if (authResult.userAuthInfo().isEmpty()) {
|
||||
return RdapAuthorization.PUBLIC_AUTHORIZATION;
|
||||
}
|
||||
UserAuthInfo userAuthInfo = authResult.userAuthInfo().get();
|
||||
|
||||
@@ -66,7 +66,7 @@ public class RdapNameserverAction extends RdapActionBase {
|
||||
Host.class,
|
||||
pathSearchString,
|
||||
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
|
||||
if (!host.isPresent() || !isAuthorized(host.get())) {
|
||||
if (host.isEmpty() || !isAuthorized(host.get())) {
|
||||
// RFC7480 5.3 - if the server wishes to respond that it doesn't have data satisfying the
|
||||
// query, it MUST reply with 404 response code.
|
||||
//
|
||||
|
||||
@@ -175,7 +175,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
RdapSearchPattern partialStringQuery) {
|
||||
Optional<Domain> domain =
|
||||
loadByForeignKeyCached(Domain.class, partialStringQuery.getSuffix(), getRequestTime());
|
||||
if (!domain.isPresent()) {
|
||||
if (domain.isEmpty()) {
|
||||
// Don't allow wildcards with suffixes which are not domains we manage. That would risk a
|
||||
// table scan in many easily foreseeable cases. The user might ask for ns*.zombo.com,
|
||||
// forcing us to query for all hosts beginning with ns, then filter for those ending in
|
||||
|
||||
@@ -121,7 +121,7 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
|
||||
*/
|
||||
protected boolean shouldBeVisible(EppResource eppResource) {
|
||||
return isAuthorized(eppResource)
|
||||
&& (!registrarParam.isPresent()
|
||||
&& (registrarParam.isEmpty()
|
||||
|| registrarParam.get().equals(eppResource.getPersistedCurrentSponsorRegistrarId()));
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
|
||||
*/
|
||||
protected boolean shouldBeVisible(Registrar registrar) {
|
||||
return isAuthorized(registrar)
|
||||
&& (!registrarParam.isPresent() || registrarParam.get().equals(registrar.getRegistrarId()));
|
||||
&& (registrarParam.isEmpty() || registrarParam.get().equals(registrar.getRegistrarId()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user