mirror of
https://github.com/google/nomulus
synced 2026-07-15 04:22:24 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5fb95f38ed | |||
| dfe8e24761 | |||
| bd30fcc81c | |||
| 8cecc8d3a8 | |||
| c5a39bccc5 | |||
| a90a117341 | |||
| b40ad54daf | |||
| b4d239c329 | |||
| daa7ab3bfa | |||
| 56cd2ad282 | |||
| 0472dda860 |
@@ -14,30 +14,71 @@
|
||||
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { AppComponent } from './app.component';
|
||||
import { MaterialModule } from './material.module';
|
||||
import { BackendService } from './shared/services/backend.service';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { routes } from './app-routing.module';
|
||||
import { AppModule } from './app.module';
|
||||
import { PocReminderComponent } from './shared/components/pocReminder/pocReminder.component';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { UserData, UserDataService } from './shared/services/userData.service';
|
||||
import { Registrar, RegistrarService } from './registrar/registrar.service';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { signal, WritableSignal } from '@angular/core';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
let component: AppComponent;
|
||||
let fixture: ComponentFixture<AppComponent>;
|
||||
let mockRegistrarService: {
|
||||
registrar: WritableSignal<Partial<Registrar> | null | undefined>;
|
||||
registrarId: WritableSignal<string>;
|
||||
registrars: WritableSignal<Array<Partial<Registrar>>>;
|
||||
};
|
||||
let mockUserDataService: { userData: WritableSignal<Partial<UserData>> };
|
||||
let mockSnackBar: jasmine.SpyObj<MatSnackBar>;
|
||||
|
||||
const dummyPocReminderComponent = class {}; // Dummy class for type checking
|
||||
|
||||
beforeEach(async () => {
|
||||
mockRegistrarService = {
|
||||
registrar: signal<Registrar | null | undefined>(undefined),
|
||||
registrarId: signal('123'),
|
||||
registrars: signal([]),
|
||||
};
|
||||
|
||||
mockUserDataService = {
|
||||
userData: signal({
|
||||
globalRole: 'NONE',
|
||||
}),
|
||||
};
|
||||
|
||||
mockSnackBar = jasmine.createSpyObj('MatSnackBar', ['openFromComponent']);
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [AppComponent],
|
||||
imports: [
|
||||
MaterialModule,
|
||||
BrowserAnimationsModule,
|
||||
AppRoutingModule,
|
||||
MatSidenavModule,
|
||||
NoopAnimationsModule,
|
||||
MatSnackBarModule,
|
||||
AppModule,
|
||||
RouterModule.forRoot(routes),
|
||||
],
|
||||
providers: [
|
||||
BackendService,
|
||||
{ provide: RegistrarService, useValue: mockRegistrarService },
|
||||
{ provide: UserDataService, useValue: mockUserDataService },
|
||||
{ provide: MatSnackBar, useValue: mockSnackBar },
|
||||
{ provide: PocReminderComponent, useClass: dummyPocReminderComponent },
|
||||
provideHttpClient(),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(AppComponent);
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jasmine.clock().uninstall();
|
||||
});
|
||||
|
||||
it('should create the app', () => {
|
||||
@@ -46,4 +87,51 @@ describe('AppComponent', () => {
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('PoC Verification Reminder', () => {
|
||||
beforeEach(() => {
|
||||
jasmine.clock().install();
|
||||
});
|
||||
|
||||
it('should open snackbar if lastPocVerificationDate is older than one year', fakeAsync(() => {
|
||||
const MOCK_TODAY = new Date('2024-07-15T10:00:00.000Z');
|
||||
jasmine.clock().mockDate(MOCK_TODAY);
|
||||
|
||||
const twoYearsAgo = new Date(MOCK_TODAY);
|
||||
twoYearsAgo.setFullYear(MOCK_TODAY.getFullYear() - 2);
|
||||
|
||||
mockRegistrarService.registrar.set({
|
||||
lastPocVerificationDate: twoYearsAgo.toISOString(),
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
TestBed.flushEffects();
|
||||
|
||||
expect(mockSnackBar.openFromComponent).toHaveBeenCalledWith(
|
||||
PocReminderComponent,
|
||||
{
|
||||
horizontalPosition: 'center',
|
||||
verticalPosition: 'top',
|
||||
duration: 1000000000,
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
it('should NOT open snackbar if lastPocVerificationDate is within last year', fakeAsync(() => {
|
||||
const MOCK_TODAY = new Date('2024-07-15T10:00:00.000Z');
|
||||
jasmine.clock().mockDate(MOCK_TODAY);
|
||||
|
||||
const sixMonthsAgo = new Date(MOCK_TODAY);
|
||||
sixMonthsAgo.setMonth(MOCK_TODAY.getMonth() - 6);
|
||||
|
||||
mockRegistrarService.registrar.set({
|
||||
lastPocVerificationDate: sixMonthsAgo.toISOString(),
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
TestBed.flushEffects();
|
||||
|
||||
expect(mockSnackBar.openFromComponent).not.toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,13 +12,15 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { AfterViewInit, Component, ViewChild } from '@angular/core';
|
||||
import { AfterViewInit, Component, effect, ViewChild } from '@angular/core';
|
||||
import { MatSidenav } from '@angular/material/sidenav';
|
||||
import { NavigationEnd, Router } from '@angular/router';
|
||||
import { RegistrarService } from './registrar/registrar.service';
|
||||
import { BreakPointObserverService } from './shared/services/breakPoint.service';
|
||||
import { GlobalLoaderService } from './shared/services/globalLoader.service';
|
||||
import { UserDataService } from './shared/services/userData.service';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { PocReminderComponent } from './shared/components/pocReminder/pocReminder.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -35,8 +37,28 @@ export class AppComponent implements AfterViewInit {
|
||||
protected userDataService: UserDataService,
|
||||
protected globalLoader: GlobalLoaderService,
|
||||
protected breakpointObserver: BreakPointObserverService,
|
||||
private router: Router
|
||||
) {}
|
||||
private router: Router,
|
||||
private _snackBar: MatSnackBar
|
||||
) {
|
||||
effect(() => {
|
||||
const registrar = this.registrarService.registrar();
|
||||
const oneYearAgo = new Date();
|
||||
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
|
||||
oneYearAgo.setHours(0, 0, 0, 0);
|
||||
if (
|
||||
registrar &&
|
||||
registrar.lastPocVerificationDate &&
|
||||
new Date(registrar.lastPocVerificationDate) < oneYearAgo &&
|
||||
this.userDataService?.userData()?.globalRole === 'NONE'
|
||||
) {
|
||||
this._snackBar.openFromComponent(PocReminderComponent, {
|
||||
horizontalPosition: 'center',
|
||||
verticalPosition: 'top',
|
||||
duration: 1000000000,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.router.events.subscribe((event) => {
|
||||
|
||||
@@ -60,6 +60,7 @@ import { TldsComponent } from './tlds/tlds.component';
|
||||
import { ForceFocusDirective } from './shared/directives/forceFocus.directive';
|
||||
import RdapComponent from './settings/rdap/rdap.component';
|
||||
import RdapEditComponent from './settings/rdap/rdapEdit.component';
|
||||
import { PocReminderComponent } from './shared/components/pocReminder/pocReminder.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [SelectedRegistrarWrapper],
|
||||
@@ -86,6 +87,7 @@ export class SelectedRegistrarModule {}
|
||||
RdapComponent,
|
||||
RdapEditComponent,
|
||||
ReasonDialogComponent,
|
||||
PocReminderComponent,
|
||||
RegistrarComponent,
|
||||
RegistrarDetailsComponent,
|
||||
RegistrarSelectorComponent,
|
||||
|
||||
@@ -57,7 +57,7 @@ export class NavigationComponent {
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe();
|
||||
this.subscription && this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
getElementId(node: RouteWithIcon) {
|
||||
|
||||
@@ -71,6 +71,7 @@ export interface Registrar
|
||||
registrarName: string;
|
||||
registryLockAllowed?: boolean;
|
||||
type?: string;
|
||||
lastPocVerificationDate?: string;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
|
||||
@@ -16,11 +16,7 @@ import { Component, effect, ViewEncapsulation } from '@angular/core';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { take } from 'rxjs';
|
||||
import { RegistrarService } from 'src/app/registrar/registrar.service';
|
||||
import {
|
||||
ContactService,
|
||||
contactTypeToViewReadyContact,
|
||||
ViewReadyContact,
|
||||
} from './contact.service';
|
||||
import { ContactService, ViewReadyContact } from './contact.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-contact',
|
||||
|
||||
@@ -83,7 +83,7 @@ export class ContactService {
|
||||
: contactTypeToViewReadyContact({
|
||||
emailAddress: '',
|
||||
name: '',
|
||||
types: ['ADMIN'],
|
||||
types: ['TECH'],
|
||||
faxNumber: '',
|
||||
phoneNumber: '',
|
||||
registrarId: '',
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
[required]="true"
|
||||
[(ngModel)]="contactService.contactInEdit.emailAddress"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
[disabled]="emailAddressIsDisabled()"
|
||||
/>
|
||||
</mat-form-field>
|
||||
|
||||
@@ -85,14 +86,18 @@
|
||||
<mat-icon color="accent">error</mat-icon>Required to select at least one
|
||||
</p>
|
||||
<div class="">
|
||||
<mat-checkbox
|
||||
<ng-container
|
||||
*ngFor="let contactType of contactTypeToTextMap | keyvalue"
|
||||
[checked]="checkboxIsChecked(contactType.key)"
|
||||
(change)="checkboxOnChange($event, contactType.key)"
|
||||
[disabled]="checkboxIsDisabled(contactType.key)"
|
||||
>
|
||||
{{ contactType.value }}
|
||||
</mat-checkbox>
|
||||
<mat-checkbox
|
||||
*ngIf="shouldDisplayCheckbox(contactType.key)"
|
||||
[checked]="checkboxIsChecked(contactType.key)"
|
||||
(change)="checkboxOnChange($event, contactType.key)"
|
||||
[disabled]="checkboxIsDisabled(contactType.key)"
|
||||
>
|
||||
{{ contactType.value }}
|
||||
</mat-checkbox>
|
||||
</ng-container>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -82,6 +82,10 @@ export class ContactDetailsComponent {
|
||||
});
|
||||
}
|
||||
|
||||
shouldDisplayCheckbox(type: string) {
|
||||
return type !== 'ADMIN' || this.checkboxIsChecked(type);
|
||||
}
|
||||
|
||||
checkboxIsChecked(type: string) {
|
||||
return this.contactService.contactInEdit.types.includes(
|
||||
type as contactType
|
||||
@@ -89,6 +93,9 @@ export class ContactDetailsComponent {
|
||||
}
|
||||
|
||||
checkboxIsDisabled(type: string) {
|
||||
if (type === 'ADMIN') {
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
this.contactService.contactInEdit.types.length === 1 &&
|
||||
this.contactService.contactInEdit.types[0] === (type as contactType)
|
||||
@@ -105,4 +112,8 @@ export class ContactDetailsComponent {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
emailAddressIsDisabled() {
|
||||
return this.contactService.contactInEdit.types.includes('ADMIN');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="console-app__pocReminder">
|
||||
<p class="">
|
||||
Please take a moment to complete annual review of
|
||||
<a routerLink="/settings">contacts</a>.
|
||||
</p>
|
||||
<span matSnackBarActions>
|
||||
<button mat-button matSnackBarAction (click)="confirmReviewed()">
|
||||
Confirm reviewed
|
||||
</button>
|
||||
<button mat-button matSnackBarAction (click)="snackBarRef.dismiss()">
|
||||
Close
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
.console-app__pocReminder {
|
||||
a {
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { MatSnackBar, MatSnackBarRef } from '@angular/material/snack-bar';
|
||||
import { RegistrarService } from '../../../registrar/registrar.service';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'app-poc-reminder',
|
||||
templateUrl: './pocReminder.component.html',
|
||||
styleUrls: ['./pocReminder.component.scss'],
|
||||
standalone: false,
|
||||
})
|
||||
export class PocReminderComponent {
|
||||
constructor(
|
||||
public snackBarRef: MatSnackBarRef<PocReminderComponent>,
|
||||
private registrarService: RegistrarService,
|
||||
private _snackBar: MatSnackBar
|
||||
) {}
|
||||
|
||||
confirmReviewed() {
|
||||
if (this.registrarService.registrar()) {
|
||||
const todayMidnight = new Date();
|
||||
todayMidnight.setHours(0, 0, 0, 0);
|
||||
this.registrarService
|
||||
// @ts-ignore - if check above won't allow empty object to be submitted
|
||||
.updateRegistrar({
|
||||
...this.registrarService.registrar(),
|
||||
lastPocVerificationDate: todayMidnight.toISOString(),
|
||||
})
|
||||
.subscribe({
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this._snackBar.open(err.error || err.message);
|
||||
},
|
||||
next: () => {
|
||||
this.snackBarRef.dismiss();
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,7 @@ def fragileTestPatterns = [
|
||||
// Currently changes a global configuration parameter that for some reason
|
||||
// results in timestamp inversions for other tests. TODO(mmuller): fix.
|
||||
"google/registry/flows/host/HostInfoFlowTest.*",
|
||||
"google/registry/beam/common/RegistryPipelineWorkerInitializerTest.*",
|
||||
] + dockerIncompatibleTestPatterns
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -172,7 +172,7 @@ public record BillingEvent(
|
||||
.minusDays(1)
|
||||
.toString(),
|
||||
billingId(),
|
||||
registrarId(),
|
||||
"",
|
||||
String.format("%s | TLD: %s | TERM: %d-year", action(), tld(), years()),
|
||||
amount(),
|
||||
currency(),
|
||||
|
||||
+24
-34
@@ -36,8 +36,6 @@ import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.flows.custom.CustomLogicFactoryModule;
|
||||
import google.registry.flows.custom.CustomLogicModule;
|
||||
import google.registry.flows.domain.DomainPricingLogic;
|
||||
import google.registry.flows.domain.DomainPricingLogic.AllocationTokenInvalidForCurrencyException;
|
||||
import google.registry.flows.domain.DomainPricingLogic.AllocationTokenInvalidForPremiumNameException;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingBase.Flag;
|
||||
import google.registry.model.billing.BillingCancellation;
|
||||
@@ -389,38 +387,30 @@ public class ExpandBillingRecurrencesPipeline implements Serializable {
|
||||
// It is OK to always create a OneTime, even though the domain might be deleted or transferred
|
||||
// later during autorenew grace period, as a cancellation will always be written out in those
|
||||
// instances.
|
||||
BillingEvent billingEvent = null;
|
||||
try {
|
||||
billingEvent =
|
||||
new BillingEvent.Builder()
|
||||
.setBillingTime(billingTime)
|
||||
.setRegistrarId(billingRecurrence.getRegistrarId())
|
||||
// Determine the cost for a one-year renewal.
|
||||
.setCost(
|
||||
domainPricingLogic
|
||||
.getRenewPrice(
|
||||
tld,
|
||||
billingRecurrence.getTargetId(),
|
||||
eventTime,
|
||||
1,
|
||||
billingRecurrence,
|
||||
Optional.empty())
|
||||
.getRenewCost())
|
||||
.setEventTime(eventTime)
|
||||
.setFlags(union(billingRecurrence.getFlags(), Flag.SYNTHETIC))
|
||||
.setDomainHistory(historyEntry)
|
||||
.setPeriodYears(1)
|
||||
.setReason(billingRecurrence.getReason())
|
||||
.setSyntheticCreationTime(endTime)
|
||||
.setCancellationMatchingBillingEvent(billingRecurrence)
|
||||
.setTargetId(billingRecurrence.getTargetId())
|
||||
.build();
|
||||
} catch (AllocationTokenInvalidForCurrencyException
|
||||
| AllocationTokenInvalidForPremiumNameException e) {
|
||||
// This should not be reached since we are not using an allocation token
|
||||
return;
|
||||
}
|
||||
results.add(billingEvent);
|
||||
results.add(
|
||||
new BillingEvent.Builder()
|
||||
.setBillingTime(billingTime)
|
||||
.setRegistrarId(billingRecurrence.getRegistrarId())
|
||||
// Determine the cost for a one-year renewal.
|
||||
.setCost(
|
||||
domainPricingLogic
|
||||
.getRenewPrice(
|
||||
tld,
|
||||
billingRecurrence.getTargetId(),
|
||||
eventTime,
|
||||
1,
|
||||
billingRecurrence,
|
||||
Optional.empty())
|
||||
.getRenewCost())
|
||||
.setEventTime(eventTime)
|
||||
.setFlags(union(billingRecurrence.getFlags(), Flag.SYNTHETIC))
|
||||
.setDomainHistory(historyEntry)
|
||||
.setPeriodYears(1)
|
||||
.setReason(billingRecurrence.getReason())
|
||||
.setSyntheticCreationTime(endTime)
|
||||
.setCancellationMatchingBillingEvent(billingRecurrence)
|
||||
.setTargetId(billingRecurrence.getTargetId())
|
||||
.build());
|
||||
}
|
||||
results.add(
|
||||
billingRecurrence
|
||||
|
||||
@@ -40,6 +40,8 @@ public class RegistryPipelineWorkerInitializer implements JvmInitializer {
|
||||
|
||||
@Override
|
||||
public void beforeProcessing(PipelineOptions options) {
|
||||
// TODO(b/416299900): remove next line after GAE is removed.
|
||||
System.setProperty("google.registry.jetty", "true");
|
||||
RegistryPipelineOptions registryOptions = options.as(RegistryPipelineOptions.class);
|
||||
RegistryEnvironment environment = registryOptions.getRegistryEnvironment();
|
||||
if (environment == null || environment.equals(RegistryEnvironment.UNITTEST)) {
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package google.registry.flows.domain;
|
||||
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
@@ -42,6 +41,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
@@ -55,14 +55,7 @@ import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.flows.custom.DomainCheckFlowCustomLogic;
|
||||
import google.registry.flows.custom.DomainCheckFlowCustomLogic.BeforeResponseParameters;
|
||||
import google.registry.flows.custom.DomainCheckFlowCustomLogic.BeforeResponseReturnData;
|
||||
import google.registry.flows.domain.DomainPricingLogic.AllocationTokenInvalidForPremiumNameException;
|
||||
import google.registry.flows.domain.token.AllocationTokenDomainCheckResults;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForCommandException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.billing.BillingRecurrence;
|
||||
@@ -87,7 +80,6 @@ import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.model.tld.label.ReservationType;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.pricing.PricingEngineProxy;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Collection;
|
||||
@@ -131,6 +123,8 @@ import org.joda.time.DateTime;
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_CHECK)
|
||||
public final class DomainCheckFlow implements TransactionalFlow {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final String STANDARD_FEE_RESPONSE_CLASS = "STANDARD";
|
||||
private static final String STANDARD_PROMOTION_FEE_RESPONSE_CLASS = "STANDARD PROMOTION";
|
||||
|
||||
@@ -146,7 +140,6 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject Clock clock;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject AllocationTokenFlowUtils allocationTokenFlowUtils;
|
||||
@Inject DomainCheckFlowCustomLogic flowCustomLogic;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
|
||||
@@ -195,36 +188,15 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||
existingDomains.size() == parsedDomains.size()
|
||||
? ImmutableSet.of()
|
||||
: getBsaBlockedDomains(parsedDomains.values(), now);
|
||||
Optional<AllocationTokenExtension> allocationTokenExtension =
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class);
|
||||
Optional<AllocationTokenDomainCheckResults> tokenDomainCheckResults =
|
||||
allocationTokenExtension.map(
|
||||
tokenExtension ->
|
||||
allocationTokenFlowUtils.checkDomainsWithToken(
|
||||
ImmutableList.copyOf(parsedDomains.values()),
|
||||
tokenExtension.getAllocationToken(),
|
||||
registrarId,
|
||||
now));
|
||||
|
||||
ImmutableList.Builder<DomainCheck> checksBuilder = new ImmutableList.Builder<>();
|
||||
ImmutableSet.Builder<String> availableDomains = new ImmutableSet.Builder<>();
|
||||
ImmutableMap<String, TldState> tldStates =
|
||||
Maps.toMap(seenTlds, tld -> Tld.get(tld).getTldState(now));
|
||||
ImmutableMap<InternetDomainName, String> domainCheckResults =
|
||||
tokenDomainCheckResults
|
||||
.map(AllocationTokenDomainCheckResults::domainCheckResults)
|
||||
.orElse(ImmutableMap.of());
|
||||
Optional<AllocationToken> allocationToken =
|
||||
tokenDomainCheckResults.flatMap(AllocationTokenDomainCheckResults::token);
|
||||
for (String domainName : domainNames) {
|
||||
Optional<String> message =
|
||||
getMessageForCheck(
|
||||
parsedDomains.get(domainName),
|
||||
existingDomains,
|
||||
bsaBlockedDomainNames,
|
||||
domainCheckResults,
|
||||
tldStates,
|
||||
allocationToken);
|
||||
domainName, existingDomains, bsaBlockedDomainNames, tldStates, parsedDomains, now);
|
||||
boolean isAvailable = message.isEmpty();
|
||||
checksBuilder.add(DomainCheck.create(isAvailable, domainName, message.orElse(null)));
|
||||
if (isAvailable) {
|
||||
@@ -237,11 +209,7 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||
.setDomainChecks(checksBuilder.build())
|
||||
.setResponseExtensions(
|
||||
getResponseExtensions(
|
||||
parsedDomains,
|
||||
existingDomains,
|
||||
availableDomains.build(),
|
||||
now,
|
||||
allocationToken))
|
||||
parsedDomains, existingDomains, availableDomains.build(), now))
|
||||
.setAsOfDate(now)
|
||||
.build());
|
||||
return responseBuilder
|
||||
@@ -251,10 +219,39 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||
}
|
||||
|
||||
private Optional<String> getMessageForCheck(
|
||||
String domainName,
|
||||
ImmutableMap<String, VKey<Domain>> existingDomains,
|
||||
ImmutableSet<InternetDomainName> bsaBlockedDomainNames,
|
||||
ImmutableMap<String, TldState> tldStates,
|
||||
ImmutableMap<String, InternetDomainName> parsedDomains,
|
||||
DateTime now) {
|
||||
InternetDomainName idn = parsedDomains.get(domainName);
|
||||
Optional<AllocationToken> token;
|
||||
try {
|
||||
// Which token we use may vary based on the domain -- a provided token may be invalid for
|
||||
// some domains, or there may be DEFAULT PROMO tokens only applicable on some domains
|
||||
token =
|
||||
AllocationTokenFlowUtils.loadTokenFromExtensionOrGetDefault(
|
||||
registrarId,
|
||||
now,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class),
|
||||
Tld.get(idn.parent().toString()),
|
||||
domainName,
|
||||
FeeQueryCommandExtensionItem.CommandName.CREATE);
|
||||
} catch (AllocationTokenFlowUtils.NonexistentAllocationTokenException
|
||||
| AllocationTokenFlowUtils.AllocationTokenInvalidException e) {
|
||||
// The provided token was catastrophically invalid in some way
|
||||
logger.atInfo().withCause(e).log("Cannot load/use allocation token.");
|
||||
return Optional.of(e.getMessage());
|
||||
}
|
||||
return getMessageForCheckWithToken(
|
||||
idn, existingDomains, bsaBlockedDomainNames, tldStates, token);
|
||||
}
|
||||
|
||||
private Optional<String> getMessageForCheckWithToken(
|
||||
InternetDomainName domainName,
|
||||
ImmutableMap<String, VKey<Domain>> existingDomains,
|
||||
ImmutableSet<InternetDomainName> bsaBlockedDomains,
|
||||
ImmutableMap<InternetDomainName, String> tokenCheckResults,
|
||||
ImmutableMap<String, TldState> tldStates,
|
||||
Optional<AllocationToken> allocationToken) {
|
||||
if (existingDomains.containsKey(domainName.toString())) {
|
||||
@@ -271,11 +268,6 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||
}
|
||||
}
|
||||
}
|
||||
Optional<String> tokenResult =
|
||||
Optional.ofNullable(emptyToNull(tokenCheckResults.get(domainName)));
|
||||
if (tokenResult.isPresent()) {
|
||||
return tokenResult;
|
||||
}
|
||||
if (isRegisterBsaCreate(domainName, allocationToken)
|
||||
|| !bsaBlockedDomains.contains(domainName)) {
|
||||
return Optional.empty();
|
||||
@@ -290,8 +282,7 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||
ImmutableMap<String, InternetDomainName> domainNames,
|
||||
ImmutableMap<String, VKey<Domain>> existingDomains,
|
||||
ImmutableSet<String> availableDomains,
|
||||
DateTime now,
|
||||
Optional<AllocationToken> allocationToken)
|
||||
DateTime now)
|
||||
throws EppException {
|
||||
Optional<FeeCheckCommandExtension> feeCheckOpt =
|
||||
eppInput.getSingleExtension(FeeCheckCommandExtension.class);
|
||||
@@ -309,84 +300,24 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||
RegistryConfig.getTieredPricingPromotionRegistrarIds().contains(registrarId);
|
||||
for (FeeCheckCommandExtensionItem feeCheckItem : feeCheck.getItems()) {
|
||||
for (String domainName : getDomainNamesToCheckForFee(feeCheckItem, domainNames.keySet())) {
|
||||
Optional<AllocationToken> defaultToken =
|
||||
DomainFlowUtils.checkForDefaultToken(
|
||||
Tld.get(InternetDomainName.from(domainName).parent().toString()),
|
||||
domainName,
|
||||
feeCheckItem.getCommandName(),
|
||||
registrarId,
|
||||
now);
|
||||
FeeCheckResponseExtensionItem.Builder<?> builder = feeCheckItem.createResponseBuilder();
|
||||
Optional<Domain> domain = Optional.ofNullable(domainObjs.get(domainName));
|
||||
Tld tld = Tld.get(domainNames.get(domainName).parent().toString());
|
||||
Optional<AllocationToken> token;
|
||||
try {
|
||||
if (allocationToken.isPresent()) {
|
||||
AllocationTokenFlowUtils.validateToken(
|
||||
InternetDomainName.from(domainName),
|
||||
allocationToken.get(),
|
||||
feeCheckItem.getCommandName(),
|
||||
registrarId,
|
||||
PricingEngineProxy.isDomainPremium(domainName, now),
|
||||
now);
|
||||
}
|
||||
handleFeeRequest(
|
||||
feeCheckItem,
|
||||
builder,
|
||||
domainNames.get(domainName),
|
||||
domain,
|
||||
feeCheck.getCurrency(),
|
||||
now,
|
||||
pricingLogic,
|
||||
allocationToken.isPresent() ? allocationToken : defaultToken,
|
||||
availableDomains.contains(domainName),
|
||||
recurrences.getOrDefault(domainName, null));
|
||||
// In the case of a registrar that is running a tiered pricing promotion, we issue two
|
||||
// responses for the CREATE fee check command: one (the default response) with the
|
||||
// non-promotional price, and one (an extra STANDARD PROMO response) with the actual
|
||||
// promotional price.
|
||||
if (defaultToken.isPresent()
|
||||
&& shouldUseTieredPricingPromotion
|
||||
&& feeCheckItem
|
||||
.getCommandName()
|
||||
.equals(FeeQueryCommandExtensionItem.CommandName.CREATE)) {
|
||||
// First, set the promotional (real) price under the STANDARD PROMO class
|
||||
builder
|
||||
.setClass(STANDARD_PROMOTION_FEE_RESPONSE_CLASS)
|
||||
.setCommand(
|
||||
FeeQueryCommandExtensionItem.CommandName.CUSTOM,
|
||||
feeCheckItem.getPhase(),
|
||||
feeCheckItem.getSubphase());
|
||||
|
||||
// Next, get the non-promotional price and set it as the standard response to the CREATE
|
||||
// fee check command
|
||||
FeeCheckResponseExtensionItem.Builder<?> nonPromotionalBuilder =
|
||||
feeCheckItem.createResponseBuilder();
|
||||
handleFeeRequest(
|
||||
feeCheckItem,
|
||||
nonPromotionalBuilder,
|
||||
domainNames.get(domainName),
|
||||
domain,
|
||||
feeCheck.getCurrency(),
|
||||
now,
|
||||
pricingLogic,
|
||||
allocationToken,
|
||||
availableDomains.contains(domainName),
|
||||
recurrences.getOrDefault(domainName, null));
|
||||
responseItems.add(
|
||||
nonPromotionalBuilder
|
||||
.setClass(STANDARD_FEE_RESPONSE_CLASS)
|
||||
.setDomainNameIfSupported(domainName)
|
||||
.build());
|
||||
}
|
||||
responseItems.add(builder.setDomainNameIfSupported(domainName).build());
|
||||
} catch (AllocationTokenInvalidForPremiumNameException
|
||||
| AllocationTokenNotValidForCommandException
|
||||
| AllocationTokenNotValidForDomainException
|
||||
| AllocationTokenNotValidForRegistrarException
|
||||
| AllocationTokenNotValidForTldException
|
||||
| AllocationTokenNotInPromotionException e) {
|
||||
// Allocation token is either not an active token or it is not valid for the EPP command,
|
||||
// registrar, domain, or TLD.
|
||||
Tld tld = Tld.get(InternetDomainName.from(domainName).parent().toString());
|
||||
// The precise token to use for this fee request may vary based on the domain or even the
|
||||
// precise command issued (some tokens may be valid only for certain actions)
|
||||
token =
|
||||
AllocationTokenFlowUtils.loadTokenFromExtensionOrGetDefault(
|
||||
registrarId,
|
||||
now,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class),
|
||||
tld,
|
||||
domainName,
|
||||
feeCheckItem.getCommandName());
|
||||
} catch (AllocationTokenFlowUtils.NonexistentAllocationTokenException
|
||||
| AllocationTokenFlowUtils.AllocationTokenInvalidException e) {
|
||||
// The provided token was catastrophically invalid in some way
|
||||
responseItems.add(
|
||||
builder
|
||||
.setDomainNameIfSupported(domainName)
|
||||
@@ -398,7 +329,60 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||
.setCurrencyIfSupported(tld.getCurrency())
|
||||
.setClass("token-not-supported")
|
||||
.build());
|
||||
continue;
|
||||
}
|
||||
handleFeeRequest(
|
||||
feeCheckItem,
|
||||
builder,
|
||||
domainNames.get(domainName),
|
||||
domain,
|
||||
feeCheck.getCurrency(),
|
||||
now,
|
||||
pricingLogic,
|
||||
token,
|
||||
availableDomains.contains(domainName),
|
||||
recurrences.getOrDefault(domainName, null));
|
||||
// In the case of a registrar that is running a tiered pricing promotion, we issue two
|
||||
// responses for the CREATE fee check command: one (the default response) with the
|
||||
// non-promotional price, and one (an extra STANDARD PROMO response) with the actual
|
||||
// promotional price.
|
||||
if (token
|
||||
.map(t -> t.getTokenType().equals(AllocationToken.TokenType.DEFAULT_PROMO))
|
||||
.orElse(false)
|
||||
&& shouldUseTieredPricingPromotion
|
||||
&& feeCheckItem
|
||||
.getCommandName()
|
||||
.equals(FeeQueryCommandExtensionItem.CommandName.CREATE)) {
|
||||
// First, set the promotional (real) price under the STANDARD PROMO class
|
||||
builder
|
||||
.setClass(STANDARD_PROMOTION_FEE_RESPONSE_CLASS)
|
||||
.setCommand(
|
||||
FeeQueryCommandExtensionItem.CommandName.CUSTOM,
|
||||
feeCheckItem.getPhase(),
|
||||
feeCheckItem.getSubphase());
|
||||
|
||||
// Next, get the non-promotional price and set it as the standard response to the CREATE
|
||||
// fee check command
|
||||
FeeCheckResponseExtensionItem.Builder<?> nonPromotionalBuilder =
|
||||
feeCheckItem.createResponseBuilder();
|
||||
handleFeeRequest(
|
||||
feeCheckItem,
|
||||
nonPromotionalBuilder,
|
||||
domainNames.get(domainName),
|
||||
domain,
|
||||
feeCheck.getCurrency(),
|
||||
now,
|
||||
pricingLogic,
|
||||
Optional.empty(),
|
||||
availableDomains.contains(domainName),
|
||||
recurrences.getOrDefault(domainName, null));
|
||||
responseItems.add(
|
||||
nonPromotionalBuilder
|
||||
.setClass(STANDARD_FEE_RESPONSE_CLASS)
|
||||
.setDomainNameIfSupported(domainName)
|
||||
.build());
|
||||
}
|
||||
responseItems.add(builder.setDomainNameIfSupported(domainName).build());
|
||||
}
|
||||
}
|
||||
return ImmutableList.of(feeCheck.createResponse(responseItems.build()));
|
||||
|
||||
@@ -133,11 +133,8 @@ import org.joda.time.Duration;
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException}
|
||||
* @error {@link AllocationTokenFlowUtils.NonexistentAllocationTokenException}
|
||||
* @error {@link google.registry.flows.exceptions.OnlyToolCanPassMetadataException}
|
||||
* @error {@link ResourceAlreadyExistsForThisClientException}
|
||||
* @error {@link ResourceCreateContentionException}
|
||||
@@ -205,7 +202,6 @@ import org.joda.time.Duration;
|
||||
* @error {@link DomainFlowUtils.UnexpectedClaimsNoticeException}
|
||||
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
||||
* @error {@link DomainFlowUtils.UnsupportedMarkTypeException}
|
||||
* @error {@link DomainPricingLogic.AllocationTokenInvalidForPremiumNameException}
|
||||
*/
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_CREATE)
|
||||
public final class DomainCreateFlow implements MutatingFlow {
|
||||
@@ -221,7 +217,6 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject DomainHistory.Builder historyBuilder;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject AllocationTokenFlowUtils allocationTokenFlowUtils;
|
||||
@Inject DomainCreateFlowCustomLogic flowCustomLogic;
|
||||
@Inject DomainFlowTmchUtils tmchUtils;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@@ -264,25 +259,20 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
}
|
||||
boolean isSunriseCreate = hasSignedMarks && (tldState == START_DATE_SUNRISE);
|
||||
Optional<AllocationToken> allocationToken =
|
||||
allocationTokenFlowUtils.verifyAllocationTokenCreateIfPresent(
|
||||
command,
|
||||
tld,
|
||||
AllocationTokenFlowUtils.loadTokenFromExtensionOrGetDefault(
|
||||
registrarId,
|
||||
now,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
boolean defaultTokenUsed = false;
|
||||
if (allocationToken.isEmpty()) {
|
||||
allocationToken =
|
||||
DomainFlowUtils.checkForDefaultToken(
|
||||
tld, command.getDomainName(), CommandName.CREATE, registrarId, now);
|
||||
if (allocationToken.isPresent()) {
|
||||
defaultTokenUsed = true;
|
||||
}
|
||||
}
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class),
|
||||
tld,
|
||||
command.getDomainName(),
|
||||
CommandName.CREATE);
|
||||
boolean defaultTokenUsed =
|
||||
allocationToken.map(t -> t.getTokenType().equals(TokenType.DEFAULT_PROMO)).orElse(false);
|
||||
boolean isAnchorTenant =
|
||||
isAnchorTenant(
|
||||
domainName, allocationToken, eppInput.getSingleExtension(MetadataExtension.class));
|
||||
verifyAnchorTenantValidPeriod(isAnchorTenant, years);
|
||||
|
||||
// Superusers can create reserved domains, force creations on domains that require a claims
|
||||
// notice without specifying a claims key, ignore the registry phase, and override blocks on
|
||||
// registering premium domains.
|
||||
@@ -416,7 +406,7 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
entitiesToSave.add(domain, domainHistory);
|
||||
if (allocationToken.isPresent() && allocationToken.get().getTokenType().isOneTimeUse()) {
|
||||
entitiesToSave.add(
|
||||
allocationTokenFlowUtils.redeemToken(
|
||||
AllocationTokenFlowUtils.redeemToken(
|
||||
allocationToken.get(), domainHistory.getHistoryEntryId()));
|
||||
}
|
||||
if (domain.shouldPublishToDns()) {
|
||||
|
||||
@@ -42,7 +42,6 @@ import static google.registry.model.tld.label.ReservationType.RESERVED_FOR_ANCHO
|
||||
import static google.registry.model.tld.label.ReservationType.RESERVED_FOR_SPECIFIC_USE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
|
||||
import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
@@ -67,7 +66,6 @@ import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.AssociationProhibitsOperationException;
|
||||
import google.registry.flows.EppException.AuthorizationErrorException;
|
||||
import google.registry.flows.EppException.CommandUseErrorException;
|
||||
import google.registry.flows.EppException.ObjectDoesNotExistException;
|
||||
@@ -77,8 +75,6 @@ import google.registry.flows.EppException.ParameterValueSyntaxErrorException;
|
||||
import google.registry.flows.EppException.RequiredParameterMissingException;
|
||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import google.registry.flows.EppException.UnimplementedOptionException;
|
||||
import google.registry.flows.domain.DomainPricingLogic.AllocationTokenInvalidForPremiumNameException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
|
||||
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.billing.BillingBase.Flag;
|
||||
@@ -101,7 +97,6 @@ import google.registry.model.domain.fee.BaseFee.FeeType;
|
||||
import google.registry.model.domain.fee.Credit;
|
||||
import google.registry.model.domain.fee.Fee;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
|
||||
import google.registry.model.domain.fee.FeeQueryResponseExtensionItem;
|
||||
import google.registry.model.domain.fee.FeeTransformCommandExtension;
|
||||
import google.registry.model.domain.fee.FeeTransformResponseExtension;
|
||||
@@ -1233,52 +1228,6 @@ public class DomainFlowUtils {
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is a valid default token to be used for a domain create command.
|
||||
*
|
||||
* <p>If there is more than one valid default token for the registration, only the first valid
|
||||
* token found on the TLD's default token list will be returned.
|
||||
*/
|
||||
public static Optional<AllocationToken> checkForDefaultToken(
|
||||
Tld tld, String domainName, CommandName commandName, String registrarId, DateTime now)
|
||||
throws EppException {
|
||||
if (isNullOrEmpty(tld.getDefaultPromoTokens())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Map<VKey<AllocationToken>, Optional<AllocationToken>> tokens =
|
||||
AllocationToken.getAll(tld.getDefaultPromoTokens());
|
||||
ImmutableList<Optional<AllocationToken>> tokenList =
|
||||
tld.getDefaultPromoTokens().stream()
|
||||
.map(tokens::get)
|
||||
.filter(Optional::isPresent)
|
||||
.collect(toImmutableList());
|
||||
checkState(
|
||||
!isNullOrEmpty(tokenList),
|
||||
"Failure while loading default TLD promotions from the database");
|
||||
// Check if any of the tokens are valid for this domain registration
|
||||
for (Optional<AllocationToken> token : tokenList) {
|
||||
try {
|
||||
AllocationTokenFlowUtils.validateToken(
|
||||
InternetDomainName.from(domainName),
|
||||
token.get(),
|
||||
commandName,
|
||||
registrarId,
|
||||
isDomainPremium(domainName, now),
|
||||
now);
|
||||
} catch (AssociationProhibitsOperationException
|
||||
| StatusProhibitsOperationException
|
||||
| AllocationTokenInvalidForPremiumNameException e) {
|
||||
// Allocation token was not valid for this registration, continue to check the next token in
|
||||
// the list
|
||||
continue;
|
||||
}
|
||||
// Only use the first valid token in the list
|
||||
return token;
|
||||
}
|
||||
// No valid default token found
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/** Resource linked to this domain does not exist. */
|
||||
static class LinkedResourcesDoNotExistException extends ObjectDoesNotExistException {
|
||||
public LinkedResourcesDoNotExistException(Class<?> type, ImmutableSet<String> resourceIds) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import google.registry.flows.ExtensionManager;
|
||||
import google.registry.flows.FlowModule.RegistrarId;
|
||||
import google.registry.flows.FlowModule.Superuser;
|
||||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.MutatingFlow;
|
||||
import google.registry.flows.TransactionalFlow;
|
||||
import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.flows.custom.DomainInfoFlowCustomLogic;
|
||||
@@ -53,6 +54,8 @@ import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.persistence.IsolationLevel;
|
||||
import google.registry.persistence.PersistenceModule;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
@@ -62,8 +65,12 @@ import org.joda.time.DateTime;
|
||||
* An EPP flow that returns information about a domain.
|
||||
*
|
||||
* <p>The registrar that owns the domain, and any registrar presenting a valid authInfo for the
|
||||
* domain, will get a rich result with all of the domain's fields. All other requests will be
|
||||
* answered with a minimal result containing only basic information about the domain.
|
||||
* domain, will get a rich result with all the domain's fields. All other requests will be answered
|
||||
* with a minimal result containing only basic information about the domain.
|
||||
*
|
||||
* <p>This implements {@link MutatingFlow} instead of {@link TransactionalFlow} as a workaround so
|
||||
* that the common workflow of "create domain, then immediately get domain info" does not run into
|
||||
* replication lag issues where the info command claims the domain does not exist.
|
||||
*
|
||||
* @error {@link google.registry.flows.FlowUtils.NotLoggedInException}
|
||||
* @error {@link google.registry.flows.FlowUtils.UnknownCurrencyEppException}
|
||||
@@ -76,7 +83,8 @@ import org.joda.time.DateTime;
|
||||
* @error {@link DomainFlowUtils.TransfersAreAlwaysForOneYearException}
|
||||
*/
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_INFO)
|
||||
public final class DomainInfoFlow implements TransactionalFlow {
|
||||
@IsolationLevel(PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ)
|
||||
public final class DomainInfoFlow implements MutatingFlow {
|
||||
|
||||
@Inject ExtensionManager extensionManager;
|
||||
@Inject ResourceCommand resourceCommand;
|
||||
|
||||
@@ -16,14 +16,13 @@ package google.registry.flows.domain;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.zeroInCurrency;
|
||||
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.validateTokenForPossiblePremiumName;
|
||||
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.discountTokenInvalidForPremiumName;
|
||||
import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.CommandUseErrorException;
|
||||
import google.registry.flows.custom.DomainPricingCustomLogic;
|
||||
import google.registry.flows.custom.DomainPricingCustomLogic.CreatePriceParameters;
|
||||
import google.registry.flows.custom.DomainPricingCustomLogic.RenewPriceParameters;
|
||||
@@ -129,9 +128,7 @@ public final class DomainPricingLogic {
|
||||
DateTime dateTime,
|
||||
int years,
|
||||
@Nullable BillingRecurrence billingRecurrence,
|
||||
Optional<AllocationToken> allocationToken)
|
||||
throws AllocationTokenInvalidForCurrencyException,
|
||||
AllocationTokenInvalidForPremiumNameException {
|
||||
Optional<AllocationToken> allocationToken) {
|
||||
checkArgument(years > 0, "Number of years must be positive");
|
||||
Money renewCost;
|
||||
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
|
||||
@@ -260,8 +257,7 @@ public final class DomainPricingLogic {
|
||||
|
||||
/** Returns the domain create cost with allocation-token-related discounts applied. */
|
||||
private Money getDomainCreateCostWithDiscount(
|
||||
DomainPrices domainPrices, int years, Optional<AllocationToken> allocationToken, Tld tld)
|
||||
throws EppException {
|
||||
DomainPrices domainPrices, int years, Optional<AllocationToken> allocationToken, Tld tld) {
|
||||
return getDomainCostWithDiscount(
|
||||
domainPrices.isPremium(),
|
||||
years,
|
||||
@@ -277,9 +273,7 @@ public final class DomainPricingLogic {
|
||||
DomainPrices domainPrices,
|
||||
DateTime dateTime,
|
||||
int years,
|
||||
Optional<AllocationToken> allocationToken)
|
||||
throws AllocationTokenInvalidForCurrencyException,
|
||||
AllocationTokenInvalidForPremiumNameException {
|
||||
Optional<AllocationToken> allocationToken) {
|
||||
// Short-circuit if the user sent an anchor-tenant or otherwise NONPREMIUM-renewal token
|
||||
if (allocationToken.isPresent()) {
|
||||
AllocationToken token = allocationToken.get();
|
||||
@@ -315,44 +309,41 @@ public final class DomainPricingLogic {
|
||||
Optional<AllocationToken> allocationToken,
|
||||
Money firstYearCost,
|
||||
Optional<Money> subsequentYearCost,
|
||||
Tld tld)
|
||||
throws AllocationTokenInvalidForCurrencyException,
|
||||
AllocationTokenInvalidForPremiumNameException {
|
||||
Tld tld) {
|
||||
checkArgument(years > 0, "Registration years to get cost for must be positive.");
|
||||
validateTokenForPossiblePremiumName(allocationToken, isPremium);
|
||||
Money totalDomainFlowCost =
|
||||
firstYearCost.plus(subsequentYearCost.orElse(firstYearCost).multipliedBy(years - 1));
|
||||
if (allocationToken.isEmpty()) {
|
||||
return totalDomainFlowCost;
|
||||
}
|
||||
AllocationToken token = allocationToken.get();
|
||||
if (discountTokenInvalidForPremiumName(token, isPremium)) {
|
||||
return totalDomainFlowCost;
|
||||
}
|
||||
if (!token.getTokenBehavior().equals(TokenBehavior.DEFAULT)) {
|
||||
return totalDomainFlowCost;
|
||||
}
|
||||
|
||||
// Apply the allocation token discount, if applicable.
|
||||
if (allocationToken.isPresent()
|
||||
&& allocationToken.get().getTokenBehavior().equals(TokenBehavior.DEFAULT)) {
|
||||
if (allocationToken.get().getDiscountPrice().isPresent()) {
|
||||
if (!tld.getCurrency()
|
||||
.equals(allocationToken.get().getDiscountPrice().get().getCurrencyUnit())) {
|
||||
throw new AllocationTokenInvalidForCurrencyException();
|
||||
}
|
||||
|
||||
int nonDiscountedYears = Math.max(0, years - allocationToken.get().getDiscountYears());
|
||||
totalDomainFlowCost =
|
||||
allocationToken
|
||||
.get()
|
||||
.getDiscountPrice()
|
||||
.get()
|
||||
.multipliedBy(allocationToken.get().getDiscountYears())
|
||||
.plus(subsequentYearCost.orElse(firstYearCost).multipliedBy(nonDiscountedYears));
|
||||
} else {
|
||||
// Assumes token has discount fraction set.
|
||||
int discountedYears = Math.min(years, allocationToken.get().getDiscountYears());
|
||||
if (token.getDiscountPrice().isPresent()
|
||||
&& tld.getCurrency().equals(token.getDiscountPrice().get().getCurrencyUnit())) {
|
||||
int nonDiscountedYears = Math.max(0, years - token.getDiscountYears());
|
||||
totalDomainFlowCost =
|
||||
token
|
||||
.getDiscountPrice()
|
||||
.get()
|
||||
.multipliedBy(token.getDiscountYears())
|
||||
.plus(subsequentYearCost.orElse(firstYearCost).multipliedBy(nonDiscountedYears));
|
||||
} else if (token.getDiscountFraction() > 0) {
|
||||
int discountedYears = Math.min(years, token.getDiscountYears());
|
||||
if (discountedYears > 0) {
|
||||
var discount =
|
||||
firstYearCost
|
||||
.plus(subsequentYearCost.orElse(firstYearCost).multipliedBy(discountedYears - 1))
|
||||
.multipliedBy(
|
||||
allocationToken.get().getDiscountFraction(), RoundingMode.HALF_EVEN);
|
||||
var discount =
|
||||
firstYearCost
|
||||
.plus(subsequentYearCost.orElse(firstYearCost).multipliedBy(discountedYears - 1))
|
||||
.multipliedBy(token.getDiscountFraction(), RoundingMode.HALF_EVEN);
|
||||
totalDomainFlowCost = totalDomainFlowCost.minus(discount);
|
||||
}
|
||||
}
|
||||
}
|
||||
return totalDomainFlowCost;
|
||||
}
|
||||
|
||||
@@ -376,18 +367,4 @@ public final class DomainPricingLogic {
|
||||
: domainPrices.getRenewCost();
|
||||
return DomainPrices.create(isPremium, createCost, renewCost);
|
||||
}
|
||||
|
||||
/** An allocation token was provided that is invalid for premium domains. */
|
||||
public static class AllocationTokenInvalidForPremiumNameException
|
||||
extends CommandUseErrorException {
|
||||
public AllocationTokenInvalidForPremiumNameException() {
|
||||
super("Token not valid for premium name");
|
||||
}
|
||||
}
|
||||
|
||||
public static class AllocationTokenInvalidForCurrencyException extends CommandUseErrorException {
|
||||
public AllocationTokenInvalidForCurrencyException() {
|
||||
super("Token and domain currencies do not match.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import static google.registry.flows.domain.DomainFlowUtils.validateRegistrationP
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyRegistrarIsActive;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
|
||||
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.maybeApplyBulkPricingRemovalToken;
|
||||
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.verifyTokenAllowedOnDomain;
|
||||
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.verifyBulkTokenAllowedOnDomain;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_RENEW;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
|
||||
@@ -124,15 +124,12 @@ import org.joda.time.Duration;
|
||||
* @error {@link RemoveBulkPricingTokenOnNonBulkPricingDomainException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException}
|
||||
* @error {@link AllocationTokenFlowUtils.NonexistentAllocationTokenException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException}
|
||||
*/
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_RENEW)
|
||||
@@ -154,7 +151,6 @@ public final class DomainRenewFlow implements MutatingFlow {
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject DomainHistory.Builder historyBuilder;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject AllocationTokenFlowUtils allocationTokenFlowUtils;
|
||||
@Inject DomainRenewFlowCustomLogic flowCustomLogic;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@Inject DomainRenewFlow() {}
|
||||
@@ -174,22 +170,17 @@ public final class DomainRenewFlow implements MutatingFlow {
|
||||
String tldStr = existingDomain.getTld();
|
||||
Tld tld = Tld.get(tldStr);
|
||||
Optional<AllocationToken> allocationToken =
|
||||
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
|
||||
existingDomain,
|
||||
tld,
|
||||
AllocationTokenFlowUtils.loadTokenFromExtensionOrGetDefault(
|
||||
registrarId,
|
||||
now,
|
||||
CommandName.RENEW,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
boolean defaultTokenUsed = false;
|
||||
if (allocationToken.isEmpty()) {
|
||||
allocationToken =
|
||||
DomainFlowUtils.checkForDefaultToken(
|
||||
tld, existingDomain.getDomainName(), CommandName.RENEW, registrarId, now);
|
||||
if (allocationToken.isPresent()) {
|
||||
defaultTokenUsed = true;
|
||||
}
|
||||
}
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class),
|
||||
tld,
|
||||
existingDomain.getDomainName(),
|
||||
CommandName.RENEW);
|
||||
boolean defaultTokenUsed =
|
||||
allocationToken
|
||||
.map(t -> t.getTokenType().equals(AllocationToken.TokenType.DEFAULT_PROMO))
|
||||
.orElse(false);
|
||||
verifyRenewAllowed(authInfo, existingDomain, command, allocationToken);
|
||||
|
||||
// If client passed an applicable static token this updates the domain
|
||||
@@ -259,7 +250,7 @@ public final class DomainRenewFlow implements MutatingFlow {
|
||||
newDomain, domainHistory, explicitRenewEvent, newAutorenewEvent, newAutorenewPollMessage);
|
||||
if (allocationToken.isPresent() && allocationToken.get().getTokenType().isOneTimeUse()) {
|
||||
entitiesToSave.add(
|
||||
allocationTokenFlowUtils.redeemToken(
|
||||
AllocationTokenFlowUtils.redeemToken(
|
||||
allocationToken.get(), domainHistory.getHistoryEntryId()));
|
||||
}
|
||||
EntityChanges entityChanges =
|
||||
@@ -327,7 +318,7 @@ public final class DomainRenewFlow implements MutatingFlow {
|
||||
}
|
||||
verifyUnitIsYears(command.getPeriod());
|
||||
// We only allow __REMOVE_BULK_PRICING__ token on bulk pricing domains for now
|
||||
verifyTokenAllowedOnDomain(existingDomain, allocationToken);
|
||||
verifyBulkTokenAllowedOnDomain(existingDomain, allocationToken);
|
||||
// If the date they specify doesn't match the expiration, fail. (This is an idempotence check).
|
||||
if (!command.getCurrentExpirationDate().equals(
|
||||
existingDomain.getRegistrationExpirationTime().toLocalDate())) {
|
||||
|
||||
@@ -54,7 +54,6 @@ import google.registry.model.billing.BillingRecurrence;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.token.AllocationTokenExtension;
|
||||
@@ -94,15 +93,12 @@ import org.joda.time.DateTime;
|
||||
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException}
|
||||
* @error {@link AllocationTokenFlowUtils.NonexistentAllocationTokenException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException}
|
||||
*/
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_TRANSFER_APPROVE)
|
||||
@@ -116,7 +112,6 @@ public final class DomainTransferApproveFlow implements MutatingFlow {
|
||||
@Inject DomainHistory.Builder historyBuilder;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@Inject AllocationTokenFlowUtils allocationTokenFlowUtils;
|
||||
@Inject EppInput eppInput;
|
||||
|
||||
@Inject DomainTransferApproveFlow() {}
|
||||
@@ -132,13 +127,8 @@ public final class DomainTransferApproveFlow implements MutatingFlow {
|
||||
extensionManager.validate();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
|
||||
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
|
||||
existingDomain,
|
||||
Tld.get(existingDomain.getTld()),
|
||||
registrarId,
|
||||
now,
|
||||
CommandName.TRANSFER,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
AllocationTokenFlowUtils.loadAllocationTokenFromExtension(
|
||||
registrarId, targetId, now, eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
verifyOptionalAuthInfo(authInfo, existingDomain);
|
||||
verifyHasPendingTransfer(existingDomain);
|
||||
verifyResourceOwnership(registrarId, existingDomain);
|
||||
|
||||
@@ -58,7 +58,6 @@ import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainCommand.Transfer;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
|
||||
import google.registry.model.domain.fee.FeeTransferCommandExtension;
|
||||
import google.registry.model.domain.fee.FeeTransformResponseExtension;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
@@ -123,15 +122,12 @@ import org.joda.time.DateTime;
|
||||
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException}
|
||||
* @error {@link AllocationTokenFlowUtils.NonexistentAllocationTokenException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException}
|
||||
*/
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_TRANSFER_REQUEST)
|
||||
@@ -154,7 +150,6 @@ public final class DomainTransferRequestFlow implements MutatingFlow {
|
||||
@Inject AsyncTaskEnqueuer asyncTaskEnqueuer;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@Inject AllocationTokenFlowUtils allocationTokenFlowUtils;
|
||||
|
||||
@Inject DomainTransferRequestFlow() {}
|
||||
|
||||
@@ -170,12 +165,10 @@ public final class DomainTransferRequestFlow implements MutatingFlow {
|
||||
extensionManager.validate();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
|
||||
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
|
||||
existingDomain,
|
||||
Tld.get(existingDomain.getTld()),
|
||||
AllocationTokenFlowUtils.loadAllocationTokenFromExtension(
|
||||
gainingClientId,
|
||||
targetId,
|
||||
now,
|
||||
CommandName.TRANSFER,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
Optional<DomainTransferRequestSuperuserExtension> superuserExtension =
|
||||
eppInput.getSingleExtension(DomainTransferRequestSuperuserExtension.class);
|
||||
|
||||
+180
-192
@@ -15,218 +15,97 @@
|
||||
package google.registry.flows.domain.token;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
|
||||
import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.AssociationProhibitsOperationException;
|
||||
import google.registry.flows.EppException.AuthorizationErrorException;
|
||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import google.registry.flows.domain.DomainPricingLogic.AllocationTokenInvalidForPremiumNameException;
|
||||
import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
|
||||
import google.registry.model.billing.BillingBase;
|
||||
import google.registry.model.billing.BillingRecurrence;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainCommand;
|
||||
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.AllocationTokenExtension;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.persistence.VKey;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Utility functions for dealing with {@link AllocationToken}s in domain flows. */
|
||||
public class AllocationTokenFlowUtils {
|
||||
|
||||
@Inject
|
||||
public AllocationTokenFlowUtils() {}
|
||||
|
||||
/**
|
||||
* Checks if the allocation token applies to the given domain names, used for domain checks.
|
||||
*
|
||||
* @return A map of domain names to domain check error response messages. If a message is present
|
||||
* for a a given domain then it does not validate with this allocation token; domains that do
|
||||
* validate have blank messages (i.e. no error).
|
||||
*/
|
||||
public AllocationTokenDomainCheckResults checkDomainsWithToken(
|
||||
List<InternetDomainName> domainNames, String token, String registrarId, DateTime now) {
|
||||
// If the token is completely invalid, return the error message for all domain names
|
||||
AllocationToken tokenEntity;
|
||||
try {
|
||||
tokenEntity = loadToken(token);
|
||||
} catch (EppException e) {
|
||||
return new AllocationTokenDomainCheckResults(
|
||||
Optional.empty(), Maps.toMap(domainNames, ignored -> e.getMessage()));
|
||||
}
|
||||
|
||||
// If the token is only invalid for some domain names (e.g. an invalid TLD), include those error
|
||||
// results for only those domain names
|
||||
ImmutableMap.Builder<InternetDomainName, String> resultsBuilder = new ImmutableMap.Builder<>();
|
||||
for (InternetDomainName domainName : domainNames) {
|
||||
try {
|
||||
validateToken(
|
||||
domainName,
|
||||
tokenEntity,
|
||||
CommandName.CREATE,
|
||||
registrarId,
|
||||
isDomainPremium(domainName.toString(), now),
|
||||
now);
|
||||
resultsBuilder.put(domainName, "");
|
||||
} catch (EppException e) {
|
||||
resultsBuilder.put(domainName, e.getMessage());
|
||||
}
|
||||
}
|
||||
return new AllocationTokenDomainCheckResults(Optional.of(tokenEntity), resultsBuilder.build());
|
||||
}
|
||||
private AllocationTokenFlowUtils() {}
|
||||
|
||||
/** Redeems a SINGLE_USE {@link AllocationToken}, returning the redeemed copy. */
|
||||
public AllocationToken redeemToken(AllocationToken token, HistoryEntryId redemptionHistoryId) {
|
||||
public static AllocationToken redeemToken(
|
||||
AllocationToken token, HistoryEntryId redemptionHistoryId) {
|
||||
checkArgument(
|
||||
token.getTokenType().isOneTimeUse(), "Only SINGLE_USE tokens can be marked as redeemed");
|
||||
return token.asBuilder().setRedemptionHistoryId(redemptionHistoryId).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a given token. The token could be invalid if it has allowed client IDs or TLDs that
|
||||
* do not include this client ID / TLD, or if the token has a promotion that is not currently
|
||||
* running, or the token is not valid for a premium name when necessary.
|
||||
*
|
||||
* @throws EppException if the token is invalid in any way
|
||||
*/
|
||||
public static void validateToken(
|
||||
InternetDomainName domainName,
|
||||
AllocationToken token,
|
||||
CommandName commandName,
|
||||
String registrarId,
|
||||
boolean isPremium,
|
||||
DateTime now)
|
||||
throws EppException {
|
||||
|
||||
// Only tokens with default behavior require validation
|
||||
if (!TokenBehavior.DEFAULT.equals(token.getTokenBehavior())) {
|
||||
return;
|
||||
}
|
||||
validateTokenForPossiblePremiumName(Optional.of(token), isPremium);
|
||||
if (!token.getAllowedEppActions().isEmpty()
|
||||
&& !token.getAllowedEppActions().contains(commandName)) {
|
||||
throw new AllocationTokenNotValidForCommandException();
|
||||
}
|
||||
if (!token.getAllowedRegistrarIds().isEmpty()
|
||||
&& !token.getAllowedRegistrarIds().contains(registrarId)) {
|
||||
throw new AllocationTokenNotValidForRegistrarException();
|
||||
}
|
||||
if (!token.getAllowedTlds().isEmpty()
|
||||
&& !token.getAllowedTlds().contains(domainName.parent().toString())) {
|
||||
throw new AllocationTokenNotValidForTldException();
|
||||
}
|
||||
if (token.getDomainName().isPresent()
|
||||
&& !token.getDomainName().get().equals(domainName.toString())) {
|
||||
throw new AllocationTokenNotValidForDomainException();
|
||||
}
|
||||
// Tokens without status transitions will just have a single-entry NOT_STARTED map, so only
|
||||
// check the status transitions map if it's non-trivial.
|
||||
if (token.getTokenStatusTransitions().size() > 1
|
||||
&& !TokenStatus.VALID.equals(token.getTokenStatusTransitions().getValueAtTime(now))) {
|
||||
throw new AllocationTokenNotInPromotionException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Validates that the given token is valid for a premium name if the name is premium. */
|
||||
public static void validateTokenForPossiblePremiumName(
|
||||
Optional<AllocationToken> token, boolean isPremium)
|
||||
throws AllocationTokenInvalidForPremiumNameException {
|
||||
if (token.isPresent()
|
||||
&& (token.get().getDiscountFraction() != 0.0 || token.get().getDiscountPrice().isPresent())
|
||||
/** Don't apply discounts on premium domains if the token isn't configured that way. */
|
||||
public static boolean discountTokenInvalidForPremiumName(
|
||||
AllocationToken token, boolean isPremium) {
|
||||
return (token.getDiscountFraction() != 0.0 || token.getDiscountPrice().isPresent())
|
||||
&& isPremium
|
||||
&& !token.get().shouldDiscountPremiums()) {
|
||||
throw new AllocationTokenInvalidForPremiumNameException();
|
||||
}
|
||||
&& !token.shouldDiscountPremiums();
|
||||
}
|
||||
|
||||
/** Loads a given token and validates that it is not redeemed */
|
||||
private static AllocationToken loadToken(String token) throws EppException {
|
||||
if (Strings.isNullOrEmpty(token)) {
|
||||
// We load the token directly from the input XML. If it's null or empty we should throw
|
||||
// an InvalidAllocationTokenException before the database load attempt fails.
|
||||
// See https://tools.ietf.org/html/draft-ietf-regext-allocation-token-04#section-2.1
|
||||
throw new InvalidAllocationTokenException();
|
||||
}
|
||||
|
||||
Optional<AllocationToken> maybeTokenEntity = AllocationToken.maybeGetStaticTokenInstance(token);
|
||||
if (maybeTokenEntity.isPresent()) {
|
||||
return maybeTokenEntity.get();
|
||||
}
|
||||
|
||||
// TODO(b/368069206): `reTransact` needed by tests only.
|
||||
maybeTokenEntity =
|
||||
tm().reTransact(() -> tm().loadByKeyIfPresent(VKey.create(AllocationToken.class, token)));
|
||||
|
||||
if (maybeTokenEntity.isEmpty()) {
|
||||
throw new InvalidAllocationTokenException();
|
||||
}
|
||||
if (maybeTokenEntity.get().isRedeemed()) {
|
||||
throw new AlreadyRedeemedAllocationTokenException();
|
||||
}
|
||||
return maybeTokenEntity.get();
|
||||
}
|
||||
|
||||
/** Verifies and returns the allocation token if one is specified, otherwise does nothing. */
|
||||
public Optional<AllocationToken> verifyAllocationTokenCreateIfPresent(
|
||||
DomainCommand.Create command,
|
||||
Tld tld,
|
||||
/** Loads and verifies the allocation token if one is specified, otherwise does nothing. */
|
||||
public static Optional<AllocationToken> loadAllocationTokenFromExtension(
|
||||
String registrarId,
|
||||
String domainName,
|
||||
DateTime now,
|
||||
Optional<AllocationTokenExtension> extension)
|
||||
throws EppException {
|
||||
throws NonexistentAllocationTokenException, AllocationTokenInvalidException {
|
||||
if (extension.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
AllocationToken tokenEntity = loadToken(extension.get().getAllocationToken());
|
||||
validateToken(
|
||||
InternetDomainName.from(command.getDomainName()),
|
||||
tokenEntity,
|
||||
CommandName.CREATE,
|
||||
registrarId,
|
||||
isDomainPremium(command.getDomainName(), now),
|
||||
now);
|
||||
return Optional.of(tokenEntity);
|
||||
return Optional.of(
|
||||
loadAndValidateToken(extension.get().getAllocationToken(), registrarId, domainName, now));
|
||||
}
|
||||
|
||||
/** Verifies and returns the allocation token if one is specified, otherwise does nothing. */
|
||||
public Optional<AllocationToken> verifyAllocationTokenIfPresent(
|
||||
Domain existingDomain,
|
||||
Tld tld,
|
||||
/**
|
||||
* Loads the relevant token, if present, for the given extension + request.
|
||||
*
|
||||
* <p>This may be the allocation token provided in the request, if it is present and valid for the
|
||||
* request. Otherwise, it may be a default allocation token if one is present and valid for the
|
||||
* request.
|
||||
*/
|
||||
public static Optional<AllocationToken> loadTokenFromExtensionOrGetDefault(
|
||||
String registrarId,
|
||||
DateTime now,
|
||||
CommandName commandName,
|
||||
Optional<AllocationTokenExtension> extension)
|
||||
throws EppException {
|
||||
if (extension.isEmpty()) {
|
||||
return Optional.empty();
|
||||
Optional<AllocationTokenExtension> extension,
|
||||
Tld tld,
|
||||
String domainName,
|
||||
CommandName commandName)
|
||||
throws NonexistentAllocationTokenException, AllocationTokenInvalidException {
|
||||
Optional<AllocationToken> fromExtension =
|
||||
loadAllocationTokenFromExtension(registrarId, domainName, now, extension);
|
||||
if (fromExtension.isPresent()
|
||||
&& tokenIsValidAgainstDomain(
|
||||
InternetDomainName.from(domainName), fromExtension.get(), commandName, now)) {
|
||||
return fromExtension;
|
||||
}
|
||||
AllocationToken tokenEntity = loadToken(extension.get().getAllocationToken());
|
||||
validateToken(
|
||||
InternetDomainName.from(existingDomain.getDomainName()),
|
||||
tokenEntity,
|
||||
commandName,
|
||||
registrarId,
|
||||
isDomainPremium(existingDomain.getDomainName(), now),
|
||||
now);
|
||||
return Optional.of(tokenEntity);
|
||||
return checkForDefaultToken(tld, domainName, commandName, registrarId, now);
|
||||
}
|
||||
|
||||
public static void verifyTokenAllowedOnDomain(
|
||||
/** Verifies that the given domain can have a bulk pricing token removed from it. */
|
||||
public static void verifyBulkTokenAllowedOnDomain(
|
||||
Domain domain, Optional<AllocationToken> allocationToken) throws EppException {
|
||||
|
||||
boolean domainHasBulkToken = domain.getCurrentBulkToken().isPresent();
|
||||
boolean hasRemoveBulkPricingToken =
|
||||
allocationToken.isPresent()
|
||||
@@ -239,6 +118,11 @@ public class AllocationTokenFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the bulk pricing token from the provided domain, if applicable.
|
||||
*
|
||||
* @param allocationToken the (possibly) REMOVE_BULK_PRICING token provided by the client.
|
||||
*/
|
||||
public static Domain maybeApplyBulkPricingRemovalToken(
|
||||
Domain domain, Optional<AllocationToken> allocationToken) {
|
||||
if (allocationToken.isEmpty()
|
||||
@@ -249,7 +133,7 @@ public class AllocationTokenFlowUtils {
|
||||
BillingRecurrence newBillingRecurrence =
|
||||
tm().loadByKey(domain.getAutorenewBillingEvent())
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT)
|
||||
.setRenewalPriceBehavior(BillingBase.RenewalPriceBehavior.DEFAULT)
|
||||
.setRenewalPrice(null)
|
||||
.build();
|
||||
|
||||
@@ -267,35 +151,139 @@ public class AllocationTokenFlowUtils {
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given token is valid for the given request.
|
||||
*
|
||||
* <p>Note that if the token is not valid, that is not a catastrophic error -- we may move on to
|
||||
* trying a different token or skip token usage entirely.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static boolean tokenIsValidAgainstDomain(
|
||||
InternetDomainName domainName, AllocationToken token, CommandName commandName, DateTime now) {
|
||||
if (discountTokenInvalidForPremiumName(token, isDomainPremium(domainName.toString(), now))) {
|
||||
return false;
|
||||
}
|
||||
if (!token.getAllowedEppActions().isEmpty()
|
||||
&& !token.getAllowedEppActions().contains(commandName)) {
|
||||
return false;
|
||||
}
|
||||
if (!token.getAllowedTlds().isEmpty()
|
||||
&& !token.getAllowedTlds().contains(domainName.parent().toString())) {
|
||||
return false;
|
||||
}
|
||||
return token.getDomainName().isEmpty()
|
||||
|| token.getDomainName().get().equals(domainName.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is a valid default token to be used for a domain create command.
|
||||
*
|
||||
* <p>If there is more than one valid default token for the registration, only the first valid
|
||||
* token found on the TLD's default token list will be returned.
|
||||
*/
|
||||
private static Optional<AllocationToken> checkForDefaultToken(
|
||||
Tld tld, String domainName, CommandName commandName, String registrarId, DateTime now) {
|
||||
ImmutableList<VKey<AllocationToken>> tokensFromTld = tld.getDefaultPromoTokens();
|
||||
if (isNullOrEmpty(tokensFromTld)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Map<VKey<AllocationToken>, Optional<AllocationToken>> tokens =
|
||||
AllocationToken.getAll(tokensFromTld);
|
||||
checkState(
|
||||
!isNullOrEmpty(tokens), "Failure while loading default TLD tokens from the database");
|
||||
// Iterate over the list to maintain token ordering (since we return the first valid token)
|
||||
ImmutableList<AllocationToken> tokenList =
|
||||
tokensFromTld.stream()
|
||||
.map(tokens::get)
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(toImmutableList());
|
||||
|
||||
// Check if any of the tokens are valid for this domain registration
|
||||
for (AllocationToken token : tokenList) {
|
||||
try {
|
||||
validateTokenEntity(token, registrarId, domainName, now);
|
||||
} catch (AllocationTokenInvalidException e) {
|
||||
// Token is not valid for this registrar, etc. -- continue trying tokens
|
||||
continue;
|
||||
}
|
||||
if (tokenIsValidAgainstDomain(InternetDomainName.from(domainName), token, commandName, now)) {
|
||||
return Optional.of(token);
|
||||
}
|
||||
}
|
||||
// No valid default token found
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/** Loads a given token and validates it against the registrar, time, etc */
|
||||
private static AllocationToken loadAndValidateToken(
|
||||
String token, String registrarId, String domainName, DateTime now)
|
||||
throws NonexistentAllocationTokenException, AllocationTokenInvalidException {
|
||||
if (Strings.isNullOrEmpty(token)) {
|
||||
// We load the token directly from the input XML. If it's null or empty we should throw
|
||||
// an NonexistentAllocationTokenException before the database load attempt fails.
|
||||
// See https://tools.ietf.org/html/draft-ietf-regext-allocation-token-04#section-2.1
|
||||
throw new NonexistentAllocationTokenException();
|
||||
}
|
||||
|
||||
Optional<AllocationToken> maybeTokenEntity = AllocationToken.maybeGetStaticTokenInstance(token);
|
||||
if (maybeTokenEntity.isPresent()) {
|
||||
return maybeTokenEntity.get();
|
||||
}
|
||||
|
||||
maybeTokenEntity = AllocationToken.get(VKey.create(AllocationToken.class, token));
|
||||
if (maybeTokenEntity.isEmpty()) {
|
||||
throw new NonexistentAllocationTokenException();
|
||||
}
|
||||
AllocationToken tokenEntity = maybeTokenEntity.get();
|
||||
validateTokenEntity(tokenEntity, registrarId, domainName, now);
|
||||
return tokenEntity;
|
||||
}
|
||||
|
||||
private static void validateTokenEntity(
|
||||
AllocationToken token, String registrarId, String domainName, DateTime now)
|
||||
throws AllocationTokenInvalidException {
|
||||
if (token.isRedeemed()) {
|
||||
throw new AlreadyRedeemedAllocationTokenException();
|
||||
}
|
||||
if (!token.getAllowedRegistrarIds().isEmpty()
|
||||
&& !token.getAllowedRegistrarIds().contains(registrarId)) {
|
||||
throw new AllocationTokenNotValidForRegistrarException();
|
||||
}
|
||||
// Tokens without status transitions will just have a single-entry NOT_STARTED map, so only
|
||||
// check the status transitions map if it's non-trivial.
|
||||
if (token.getTokenStatusTransitions().size() > 1
|
||||
&& !AllocationToken.TokenStatus.VALID.equals(
|
||||
token.getTokenStatusTransitions().getValueAtTime(now))) {
|
||||
throw new AllocationTokenNotInPromotionException();
|
||||
}
|
||||
|
||||
if (token.getDomainName().isPresent() && !token.getDomainName().get().equals(domainName)) {
|
||||
throw new AllocationTokenNotValidForDomainException();
|
||||
}
|
||||
}
|
||||
|
||||
// Note: exception messages should be <= 32 characters long for domain check results
|
||||
|
||||
/** The allocation token exists but is not valid, e.g. the wrong registrar. */
|
||||
public abstract static class AllocationTokenInvalidException
|
||||
extends StatusProhibitsOperationException {
|
||||
AllocationTokenInvalidException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
/** The allocation token is not currently valid. */
|
||||
public static class AllocationTokenNotInPromotionException
|
||||
extends StatusProhibitsOperationException {
|
||||
extends AllocationTokenInvalidException {
|
||||
AllocationTokenNotInPromotionException() {
|
||||
super("Alloc token not in promo period");
|
||||
}
|
||||
}
|
||||
|
||||
/** The allocation token is not valid for this TLD. */
|
||||
public static class AllocationTokenNotValidForTldException
|
||||
extends AssociationProhibitsOperationException {
|
||||
AllocationTokenNotValidForTldException() {
|
||||
super("Alloc token invalid for TLD");
|
||||
}
|
||||
}
|
||||
|
||||
/** The allocation token is not valid for this domain. */
|
||||
public static class AllocationTokenNotValidForDomainException
|
||||
extends AssociationProhibitsOperationException {
|
||||
AllocationTokenNotValidForDomainException() {
|
||||
super("Alloc token invalid for domain");
|
||||
}
|
||||
}
|
||||
|
||||
/** The allocation token is not valid for this registrar. */
|
||||
public static class AllocationTokenNotValidForRegistrarException
|
||||
extends AssociationProhibitsOperationException {
|
||||
extends AllocationTokenInvalidException {
|
||||
AllocationTokenNotValidForRegistrarException() {
|
||||
super("Alloc token invalid for client");
|
||||
}
|
||||
@@ -303,23 +291,23 @@ public class AllocationTokenFlowUtils {
|
||||
|
||||
/** The allocation token was already redeemed. */
|
||||
public static class AlreadyRedeemedAllocationTokenException
|
||||
extends AssociationProhibitsOperationException {
|
||||
extends AllocationTokenInvalidException {
|
||||
AlreadyRedeemedAllocationTokenException() {
|
||||
super("Alloc token was already redeemed");
|
||||
}
|
||||
}
|
||||
|
||||
/** The allocation token is not valid for this EPP command. */
|
||||
public static class AllocationTokenNotValidForCommandException
|
||||
extends AssociationProhibitsOperationException {
|
||||
AllocationTokenNotValidForCommandException() {
|
||||
super("Allocation token not valid for the EPP command");
|
||||
/** The allocation token is not valid for this domain. */
|
||||
public static class AllocationTokenNotValidForDomainException
|
||||
extends AllocationTokenInvalidException {
|
||||
AllocationTokenNotValidForDomainException() {
|
||||
super("Alloc token invalid for domain");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The allocation token is invalid. */
|
||||
public static class InvalidAllocationTokenException extends AuthorizationErrorException {
|
||||
InvalidAllocationTokenException() {
|
||||
public static class NonexistentAllocationTokenException extends AuthorizationErrorException {
|
||||
NonexistentAllocationTokenException() {
|
||||
super("The allocation token is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,6 +403,9 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
*/
|
||||
DateTime lastExpiringFailoverCertNotificationSentDate = START_OF_TIME;
|
||||
|
||||
/** The time that the POCs have been reviewed last. */
|
||||
@Expose DateTime lastPocVerificationDate = START_OF_TIME;
|
||||
|
||||
/** Telephone support passcode (5-digit numeric) */
|
||||
String phonePasscode;
|
||||
|
||||
@@ -461,6 +464,10 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
return registrarName;
|
||||
}
|
||||
|
||||
public DateTime getLastPocVerificationDate() {
|
||||
return lastPocVerificationDate;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
@@ -614,6 +621,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
.putString(
|
||||
"lastExpiringFailoverCertNotificationSentDate",
|
||||
lastExpiringFailoverCertNotificationSentDate)
|
||||
.putString("lastPocVerificationDate", lastPocVerificationDate)
|
||||
.put("registrarName", registrarName)
|
||||
.put("type", type)
|
||||
.put("state", state)
|
||||
@@ -802,6 +810,12 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setLastPocVerificationDate(DateTime now) {
|
||||
checkArgumentNotNull(now, "Registrar lastPocVerificationDate cannot be null");
|
||||
getInstance().lastPocVerificationDate = now;
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
private static String calculateHash(String clientCertificate) {
|
||||
if (clientCertificate == null) {
|
||||
return null;
|
||||
|
||||
-4
@@ -276,10 +276,6 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
}
|
||||
T result = work.call();
|
||||
txn.commit();
|
||||
long duration = clock.nowUtc().getMillis() - txnInfo.transactionTime.getMillis();
|
||||
if (duration >= 100) {
|
||||
logger.atInfo().log("Transaction duration: %d milliseconds", duration);
|
||||
}
|
||||
return result;
|
||||
} catch (Throwable e) {
|
||||
// Catch a Throwable here so even Errors would lead to a rollback.
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
@@ -23,6 +24,7 @@ import static org.joda.time.DateTimeZone.UTC;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.pricing.PremiumPricingEngine.DomainPrices;
|
||||
import google.registry.tools.soy.DomainCreateSoyInfo;
|
||||
import google.registry.util.StringGenerator;
|
||||
@@ -58,9 +60,15 @@ final class CreateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
|
||||
@Override
|
||||
protected void initMutatingEppToolCommand() {
|
||||
checkArgumentNotNull(registrant, "Registrant must be specified");
|
||||
checkArgument(!admins.isEmpty(), "At least one admin must be specified");
|
||||
checkArgument(!techs.isEmpty(), "At least one tech must be specified");
|
||||
tm().transact(
|
||||
() -> {
|
||||
if (!FeatureFlag.isActiveNowOrElse(
|
||||
FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL, false)) {
|
||||
checkArgumentNotNull(registrant, "Registrant must be specified");
|
||||
checkArgument(!admins.isEmpty(), "At least one admin must be specified");
|
||||
checkArgument(!techs.isEmpty(), "At least one tech must be specified");
|
||||
}
|
||||
});
|
||||
if (isNullOrEmpty(password)) {
|
||||
password = passwordGenerator.createString(PASSWORD_LENGTH);
|
||||
}
|
||||
|
||||
+27
-7
@@ -17,6 +17,7 @@ package google.registry.ui.server.console;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
import static org.apache.http.HttpStatus.SC_OK;
|
||||
|
||||
@@ -37,6 +38,7 @@ import google.registry.util.RegistryEnvironment;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@Action(
|
||||
service = GaeService.DEFAULT,
|
||||
@@ -88,17 +90,35 @@ public class ConsoleUpdateRegistrarAction extends ConsoleApiAction {
|
||||
}
|
||||
}
|
||||
|
||||
Registrar updatedRegistrar =
|
||||
DateTime now = tm().getTransactionTime();
|
||||
DateTime newLastPocVerificationDate =
|
||||
registrarParam.getLastPocVerificationDate() == null
|
||||
? START_OF_TIME
|
||||
: registrarParam.getLastPocVerificationDate();
|
||||
|
||||
checkArgument(
|
||||
newLastPocVerificationDate.isBefore(now),
|
||||
"Invalid value of LastPocVerificationDate - value is in the future");
|
||||
|
||||
var updatedRegistrarBuilder =
|
||||
existingRegistrar
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setAllowedTlds(
|
||||
registrarParam.getAllowedTlds().stream()
|
||||
.map(DomainNameUtils::canonicalizeHostname)
|
||||
.collect(Collectors.toSet()))
|
||||
.setRegistryLockAllowed(registrarParam.isRegistryLockAllowed())
|
||||
.build();
|
||||
.setLastPocVerificationDate(newLastPocVerificationDate);
|
||||
|
||||
if (user.getUserRoles()
|
||||
.hasGlobalPermission(ConsolePermission.EDIT_REGISTRAR_DETAILS)) {
|
||||
updatedRegistrarBuilder =
|
||||
updatedRegistrarBuilder
|
||||
.setAllowedTlds(
|
||||
registrarParam.getAllowedTlds().stream()
|
||||
.map(DomainNameUtils::canonicalizeHostname)
|
||||
.collect(Collectors.toSet()))
|
||||
.setRegistryLockAllowed(registrarParam.isRegistryLockAllowed())
|
||||
.setLastPocVerificationDate(newLastPocVerificationDate);
|
||||
}
|
||||
|
||||
var updatedRegistrar = updatedRegistrarBuilder.build();
|
||||
tm().put(updatedRegistrar);
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
|
||||
@@ -169,6 +169,7 @@ public class ContactAction extends ConsoleApiAction {
|
||||
throw new ContactRequirementException(t);
|
||||
}
|
||||
}
|
||||
enforcePrimaryContactRestrictions(oldContactsByType, newContactsByType);
|
||||
ensurePhoneNumberNotRemovedForContactTypes(oldContactsByType, newContactsByType, Type.TECH);
|
||||
Optional<RegistrarPoc> domainWhoisAbuseContact =
|
||||
getDomainWhoisVisibleAbuseContact(updatedContacts);
|
||||
@@ -187,6 +188,23 @@ public class ContactAction extends ConsoleApiAction {
|
||||
checkContactRegistryLockRequirements(existingContacts, updatedContacts);
|
||||
}
|
||||
|
||||
private static void enforcePrimaryContactRestrictions(
|
||||
Multimap<Type, RegistrarPoc> oldContactsByType,
|
||||
Multimap<Type, RegistrarPoc> newContactsByType) {
|
||||
ImmutableSet<String> oldAdminEmails =
|
||||
oldContactsByType.get(Type.ADMIN).stream()
|
||||
.map(RegistrarPoc::getEmailAddress)
|
||||
.collect(toImmutableSet());
|
||||
ImmutableSet<String> newAdminEmails =
|
||||
newContactsByType.get(Type.ADMIN).stream()
|
||||
.map(RegistrarPoc::getEmailAddress)
|
||||
.collect(toImmutableSet());
|
||||
if (!newAdminEmails.containsAll(oldAdminEmails)) {
|
||||
throw new ContactRequirementException(
|
||||
"Cannot remove or change the email address of primary contacts");
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkContactRegistryLockRequirements(
|
||||
ImmutableSet<RegistrarPoc> existingContacts, ImmutableSet<RegistrarPoc> updatedContacts) {
|
||||
// Any contact(s) with new passwords must be allowed to set them
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
{@param domain: string}
|
||||
{@param period: int}
|
||||
{@param nameservers: list<string>}
|
||||
{@param registrant: string}
|
||||
{@param admins: list<string>}
|
||||
{@param techs: list<string>}
|
||||
{@param? registrant: string|null}
|
||||
{@param? admins: list<string>|null}
|
||||
{@param? techs: list<string>|null}
|
||||
{@param password: string}
|
||||
{@param? currency: string|null}
|
||||
{@param? price: string|null}
|
||||
@@ -45,13 +45,19 @@
|
||||
{/for}
|
||||
</domain:ns>
|
||||
{/if}
|
||||
<domain:registrant>{$registrant}</domain:registrant>
|
||||
{for $admin in $admins}
|
||||
<domain:contact type="admin">{$admin}</domain:contact>
|
||||
{/for}
|
||||
{for $tech in $techs}
|
||||
<domain:contact type="tech">{$tech}</domain:contact>
|
||||
{/for}
|
||||
{if $registrant != null}
|
||||
<domain:registrant>{$registrant}</domain:registrant>
|
||||
{/if}
|
||||
{if $admins != null}
|
||||
{for $admin in $admins}
|
||||
<domain:contact type="admin">{$admin}</domain:contact>
|
||||
{/for}
|
||||
{/if}
|
||||
{if $techs != null}
|
||||
{for $tech in $techs}
|
||||
<domain:contact type="tech">{$tech}</domain:contact>
|
||||
{/for}
|
||||
{/if}
|
||||
<domain:authInfo>
|
||||
<domain:pw>{$password}</domain:pw>
|
||||
</domain:authInfo>
|
||||
|
||||
@@ -85,7 +85,7 @@ class BillingEventTest {
|
||||
assertThat(invoiceKey.startDate()).isEqualTo("2017-10-01");
|
||||
assertThat(invoiceKey.endDate()).isEqualTo("2022-09-30");
|
||||
assertThat(invoiceKey.productAccountKey()).isEqualTo("12345-CRRHELLO");
|
||||
assertThat(invoiceKey.usageGroupingKey()).isEqualTo("myRegistrar");
|
||||
assertThat(invoiceKey.usageGroupingKey()).isEqualTo("");
|
||||
assertThat(invoiceKey.description()).isEqualTo("RENEW | TLD: test | TERM: 5-year");
|
||||
assertThat(invoiceKey.unitPrice()).isEqualTo(20.5);
|
||||
assertThat(invoiceKey.unitPriceCurrency()).isEqualTo("USD");
|
||||
@@ -106,7 +106,7 @@ class BillingEventTest {
|
||||
assertThat(invoiceKey.toCsv(3L))
|
||||
.isEqualTo(
|
||||
"2017-10-01,2022-09-30,12345-CRRHELLO,61.50,USD,10125,1,PURCHASE,"
|
||||
+ "myRegistrar,3,RENEW | TLD: test | TERM: 5-year,20.50,USD,");
|
||||
+ ",3,RENEW | TLD: test | TERM: 5-year,20.50,USD,");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,7 +116,7 @@ class BillingEventTest {
|
||||
assertThat(invoiceKey.toCsv(3L))
|
||||
.isEqualTo(
|
||||
"2017-10-01,,12345-CRRHELLO,61.50,USD,10125,1,PURCHASE,"
|
||||
+ "myRegistrar,3,RENEW | TLD: test | TERM: 0-year,20.50,USD,");
|
||||
+ ",3,RENEW | TLD: test | TERM: 0-year,20.50,USD,");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -199,6 +199,36 @@ class InvoicingPipelineTest {
|
||||
0,
|
||||
"USD",
|
||||
20.0,
|
||||
""),
|
||||
google.registry.beam.billing.BillingEvent.create(
|
||||
15,
|
||||
DateTime.parse("2017-10-02T00:00:00.0Z"),
|
||||
DateTime.parse("2017-10-04T00:00:00.0Z"),
|
||||
"theRegistrarCopy",
|
||||
"234",
|
||||
"",
|
||||
"test",
|
||||
"CREATE",
|
||||
"mydomainfromanotherclient.test",
|
||||
"REPO-ID",
|
||||
5,
|
||||
"JPY",
|
||||
70.0,
|
||||
""),
|
||||
google.registry.beam.billing.BillingEvent.create(
|
||||
16,
|
||||
DateTime.parse("2017-10-04T00:00:00Z"),
|
||||
DateTime.parse("2017-10-04T00:00:00Z"),
|
||||
"theRegistrarCopy",
|
||||
"234",
|
||||
"",
|
||||
"test",
|
||||
"RENEW",
|
||||
"mydomain2fromanotherclient.test",
|
||||
"REPO-ID",
|
||||
3,
|
||||
"USD",
|
||||
20.5,
|
||||
""));
|
||||
|
||||
private static final ImmutableMap<String, ImmutableList<String>> EXPECTED_DETAILED_REPORT_MAP =
|
||||
@@ -224,18 +254,26 @@ class InvoicingPipelineTest {
|
||||
"invoice_details_2017-10_anotherRegistrar_test.csv",
|
||||
ImmutableList.of(
|
||||
"5,2017-10-04 00:00:00 UTC,2017-10-04 00:00:00 UTC,anotherRegistrar,789,,"
|
||||
+ "test,CREATE,mydomain5.test,REPO-ID,1,USD,0.00,SUNRISE ANCHOR_TENANT"));
|
||||
+ "test,CREATE,mydomain5.test,REPO-ID,1,USD,0.00,SUNRISE ANCHOR_TENANT"),
|
||||
"invoice_details_2017-10_theRegistrarCopy_test.csv",
|
||||
ImmutableList.of(
|
||||
"15,2017-10-02 00:00:00 UTC,2017-10-04 00:00:00"
|
||||
+ " UTC,theRegistrarCopy,234,,test,CREATE,mydomainfromanotherclient.test,REPO-ID,5,JPY,70.00,",
|
||||
"16,2017-10-04 00:00:00 UTC,2017-10-04 00:00:00"
|
||||
+ " UTC,theRegistrarCopy,234,,test,RENEW,mydomain2fromanotherclient.test,REPO-ID,3,USD,20.50,"));
|
||||
|
||||
private static final ImmutableList<String> EXPECTED_INVOICE_OUTPUT =
|
||||
ImmutableList.of(
|
||||
"2017-10-01,2020-09-30,234,41.00,USD,10125,1,PURCHASE,theRegistrar,2,"
|
||||
"2017-10-01,2020-09-30,234,61.50,USD,10125,1,PURCHASE,,3,"
|
||||
+ "RENEW | TLD: test | TERM: 3-year,20.50,USD,",
|
||||
"2017-10-01,2022-09-30,234,70.00,JPY,10125,1,PURCHASE,theRegistrar,1,"
|
||||
"2017-10-01,2022-09-30,234,70.00,JPY,10125,1,PURCHASE,,1,"
|
||||
+ "CREATE | TLD: hello | TERM: 5-year,70.00,JPY,",
|
||||
"2017-10-01,,234,20.00,USD,10125,1,PURCHASE,theRegistrar,1,"
|
||||
"2017-10-01,,234,20.00,USD,10125,1,PURCHASE,,1,"
|
||||
+ "SERVER_STATUS | TLD: test | TERM: 0-year,20.00,USD,",
|
||||
"2017-10-01,2018-09-30,456,20.50,USD,10125,1,PURCHASE,bestdomains,1,"
|
||||
+ "RENEW | TLD: test | TERM: 1-year,20.50,USD,116688");
|
||||
"2017-10-01,2018-09-30,456,20.50,USD,10125,1,PURCHASE,,1,"
|
||||
+ "RENEW | TLD: test | TERM: 1-year,20.50,USD,116688",
|
||||
"2017-10-01,2022-09-30,234,70.00,JPY,10125,1,PURCHASE,,1,CREATE | TLD: test | TERM:"
|
||||
+ " 5-year,70.00,JPY,");
|
||||
|
||||
private final InvoicingPipelineOptions options =
|
||||
PipelineOptionsFactory.create().as(InvoicingPipelineOptions.class);
|
||||
@@ -355,21 +393,21 @@ class InvoicingPipelineTest {
|
||||
.isEqualTo(
|
||||
"""
|
||||
|
||||
SELECT b, r FROM BillingEvent b
|
||||
JOIN Registrar r ON b.clientId = r.registrarId
|
||||
JOIN Domain d ON b.domainRepoId = d.repoId
|
||||
JOIN Tld t ON t.tldStr = d.tld
|
||||
LEFT JOIN BillingCancellation c ON b.id = c.billingEvent
|
||||
LEFT JOIN BillingCancellation cr ON b.cancellationMatchingBillingEvent = cr.billingRecurrence
|
||||
WHERE r.billingAccountMap IS NOT NULL
|
||||
AND r.type = 'REAL'
|
||||
AND t.invoicingEnabled IS TRUE
|
||||
AND CAST(b.billingTime AS timestamp)
|
||||
BETWEEN CAST('2017-10-01T00:00:00Z' AS timestamp)
|
||||
AND CAST('2017-11-01T00:00:00Z' AS timestamp)
|
||||
AND c.id IS NULL
|
||||
AND cr.id IS NULL
|
||||
""");
|
||||
SELECT b, r FROM BillingEvent b
|
||||
JOIN Registrar r ON b.clientId = r.registrarId
|
||||
JOIN Domain d ON b.domainRepoId = d.repoId
|
||||
JOIN Tld t ON t.tldStr = d.tld
|
||||
LEFT JOIN BillingCancellation c ON b.id = c.billingEvent
|
||||
LEFT JOIN BillingCancellation cr ON b.cancellationMatchingBillingEvent = cr.billingRecurrence
|
||||
WHERE r.billingAccountMap IS NOT NULL
|
||||
AND r.type = 'REAL'
|
||||
AND t.invoicingEnabled IS TRUE
|
||||
AND CAST(b.billingTime AS timestamp)
|
||||
BETWEEN CAST('2017-10-01T00:00:00Z' AS timestamp)
|
||||
AND CAST('2017-11-01T00:00:00Z' AS timestamp)
|
||||
AND c.id IS NULL
|
||||
AND cr.id IS NULL
|
||||
""");
|
||||
}
|
||||
|
||||
/** Returns the text contents of a file under the beamBucket/results directory. */
|
||||
@@ -391,6 +429,13 @@ class InvoicingPipelineTest {
|
||||
.setBillingAccountMap(ImmutableMap.of(JPY, "234", USD, "234"))
|
||||
.build();
|
||||
persistResource(registrar1);
|
||||
Registrar registrar11 = persistNewRegistrar("theRegistrarCopy");
|
||||
registrar11 =
|
||||
registrar11
|
||||
.asBuilder()
|
||||
.setBillingAccountMap(ImmutableMap.of(JPY, "234", USD, "234"))
|
||||
.build();
|
||||
persistResource(registrar11);
|
||||
Registrar registrar2 = persistNewRegistrar("bestdomains");
|
||||
registrar2 =
|
||||
registrar2
|
||||
@@ -547,6 +592,21 @@ class InvoicingPipelineTest {
|
||||
.setDomainHistory(domainHistoryRecurrence)
|
||||
.build();
|
||||
persistResource(cancellationRecurrence);
|
||||
|
||||
// Domains created for registrar with = key but != client id.
|
||||
Domain domain14 = persistActiveDomain("mydomainfromanotherclient.test");
|
||||
Domain domain15 = persistActiveDomain("mydomain2fromanotherclient.test");
|
||||
|
||||
persistBillingEvent(
|
||||
15,
|
||||
domain14,
|
||||
registrar11,
|
||||
Reason.CREATE,
|
||||
5,
|
||||
Money.ofMajor(JPY, 70),
|
||||
DateTime.parse("2017-10-04T00:00:00.0Z"),
|
||||
DateTime.parse("2017-10-02T00:00:00.0Z"));
|
||||
persistBillingEvent(16, domain15, registrar11, Reason.RENEW, 3, Money.of(USD, 20.5));
|
||||
}
|
||||
|
||||
private static DomainHistory persistDomainHistory(Domain domain, Registrar registrar) {
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.beam.common;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import org.apache.beam.sdk.options.PipelineOptionsFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class RegistryPipelineWorkerInitializerTest {
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
RegistryPipelineOptions options =
|
||||
PipelineOptionsFactory.fromArgs(
|
||||
"--registryEnvironment=ALPHA", "--isolationOverride=TRANSACTION_SERIALIZABLE")
|
||||
.withValidation()
|
||||
.as(RegistryPipelineOptions.class);
|
||||
new RegistryPipelineWorkerInitializer().beforeProcessing(options);
|
||||
assertThat(RegistryEnvironment.isOnJetty()).isTrue();
|
||||
System.clearProperty("google.registry.jetty");
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,7 @@ public class FlowModuleTest {
|
||||
|
||||
@Test
|
||||
void givenNonMutatingFlow_thenReplicaTmIsUsed() throws EppException {
|
||||
String eppInputXmlFilename = "domain_info.xml";
|
||||
String eppInputXmlFilename = "domain_check.xml";
|
||||
FlowModule flowModule =
|
||||
new FlowModule.Builder().setEppInput(getEppInput(eppInputXmlFilename)).build();
|
||||
JpaTransactionManager tm =
|
||||
|
||||
@@ -211,8 +211,8 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
doCheckTest(
|
||||
create(true, "example1.tld", null),
|
||||
create(false, "example2.tld", "Alloc token invalid for domain"),
|
||||
create(false, "reserved.tld", "Reserved"),
|
||||
create(false, "specificuse.tld", "Reserved; alloc. token required"));
|
||||
create(false, "reserved.tld", "Alloc token invalid for domain"),
|
||||
create(false, "specificuse.tld", "Alloc token invalid for domain"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -230,8 +230,8 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "Blocked by a GlobalBlock service"),
|
||||
create(false, "example2.tld", "Alloc token invalid for domain"),
|
||||
create(false, "reserved.tld", "Reserved"),
|
||||
create(false, "specificuse.tld", "Reserved; alloc. token required"));
|
||||
create(false, "reserved.tld", "Alloc token invalid for domain"),
|
||||
create(false, "specificuse.tld", "Alloc token invalid for domain"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -257,17 +257,6 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
create(true, "example3.tld", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_oneExists_allocationTokenIsInvalid() throws Exception {
|
||||
setEppInput("domain_check_allocationtoken.xml");
|
||||
persistActiveDomain("example1.tld");
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "In use"),
|
||||
create(false, "example2.tld", "The allocation token is invalid"),
|
||||
create(false, "reserved.tld", "Reserved"),
|
||||
create(false, "specificuse.tld", "Reserved; alloc. token required"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_oneExists_allocationTokenIsValid() throws Exception {
|
||||
setEppInput("domain_check_allocationtoken.xml");
|
||||
@@ -300,24 +289,6 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
runFlowAssertResponse(loadFile("domain_check_allocationtoken_fee_anchor_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_oneExists_allocationTokenIsRedeemed() throws Exception {
|
||||
setEppInput("domain_check_allocationtoken.xml");
|
||||
Domain domain = persistActiveDomain("example1.tld");
|
||||
HistoryEntryId historyEntryId = new HistoryEntryId(domain.getRepoId(), 1L);
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRedemptionHistoryId(historyEntryId)
|
||||
.build());
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "In use"),
|
||||
create(false, "example2.tld", "Alloc token was already redeemed"),
|
||||
create(false, "reserved.tld", "Reserved"),
|
||||
create(false, "specificuse.tld", "Reserved; alloc. token required"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_oneExists_allocationTokenForReservedDomain() throws Exception {
|
||||
setEppInput("domain_check_allocationtoken.xml");
|
||||
@@ -329,9 +300,9 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
.setTokenType(SINGLE_USE)
|
||||
.build());
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "In use"),
|
||||
create(false, "example1.tld", "Alloc token invalid for domain"),
|
||||
create(false, "example2.tld", "Alloc token invalid for domain"),
|
||||
create(false, "reserved.tld", "Reserved"),
|
||||
create(false, "reserved.tld", "Alloc token invalid for domain"),
|
||||
create(true, "specificuse.tld", null));
|
||||
}
|
||||
|
||||
@@ -350,23 +321,6 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
runFlowAssertResponse(loadFile("domain_check_allocationtoken_fee_specificuse_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_oneExists_allocationTokenForWrongDomain() throws Exception {
|
||||
setEppInput("domain_check_allocationtoken.xml");
|
||||
persistActiveDomain("example1.tld");
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setDomainName("someotherdomain.tld")
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.build());
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "In use"),
|
||||
create(false, "example2.tld", "Alloc token invalid for domain"),
|
||||
create(false, "reserved.tld", "Reserved"),
|
||||
create(false, "specificuse.tld", "Reserved; alloc. token required"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_notOutOfDateToken_forSpecificDomain() throws Exception {
|
||||
setEppInput("domain_check_allocationtoken.xml");
|
||||
@@ -385,44 +339,10 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "Alloc token invalid for domain"),
|
||||
create(false, "example2.tld", "Alloc token invalid for domain"),
|
||||
create(false, "reserved.tld", "Reserved"),
|
||||
create(false, "reserved.tld", "Alloc token invalid for domain"),
|
||||
create(true, "specificuse.tld", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_outOfDateToken_forSpecificDomain() throws Exception {
|
||||
setEppInput("domain_check_allocationtoken.xml");
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDomainName("specificuse.tld")
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||
.put(clock.nowUtc().minusDays(2), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().minusDays(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "Alloc token invalid for domain"),
|
||||
create(false, "example2.tld", "Alloc token invalid for domain"),
|
||||
create(false, "reserved.tld", "Reserved"),
|
||||
create(false, "specificuse.tld", "Alloc token not in promo period"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nothingExists_reservationsOverrideInvalidAllocationTokens() throws Exception {
|
||||
setEppInput("domain_check_reserved_allocationtoken.xml");
|
||||
// Fill out these reasons
|
||||
doCheckTest(
|
||||
create(false, "collision.tld", "Cannot be delegated"),
|
||||
create(false, "reserved.tld", "Reserved"),
|
||||
create(false, "anchor.tld", "Reserved; alloc. token required"),
|
||||
create(false, "allowedinsunrise.tld", "Reserved"),
|
||||
create(false, "premiumcollision.tld", "Cannot be delegated"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_allocationTokenPromotion_singleYear() throws Exception {
|
||||
createTld("example");
|
||||
@@ -500,7 +420,75 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_allocationTokenPromotion_PremiumsNotSet() throws Exception {
|
||||
void testSuccess_allocationTokenInvalid_overridesOtherErrors() throws Exception {
|
||||
setEppInput("domain_check_allocationtoken.xml");
|
||||
persistActiveDomain("example1.tld");
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "The allocation token is invalid"),
|
||||
create(false, "example2.tld", "The allocation token is invalid"),
|
||||
create(false, "reserved.tld", "The allocation token is invalid"),
|
||||
create(false, "specificuse.tld", "The allocation token is invalid"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_allocationTokenForWrongDomain_overridesOtherConcerns() throws Exception {
|
||||
setEppInput("domain_check_allocationtoken.xml");
|
||||
persistActiveDomain("example1.tld");
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setDomainName("someotherdomain.tld")
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.build());
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "Alloc token invalid for domain"),
|
||||
create(false, "example2.tld", "Alloc token invalid for domain"),
|
||||
create(false, "reserved.tld", "Alloc token invalid for domain"),
|
||||
create(false, "specificuse.tld", "Alloc token invalid for domain"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_outOfDateToken_overridesOtherIssues() throws Exception {
|
||||
setEppInput("domain_check_allocationtoken.xml");
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDomainName("specificuse.tld")
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||
.put(clock.nowUtc().minusDays(2), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().minusDays(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "Alloc token not in promo period"),
|
||||
create(false, "example2.tld", "Alloc token not in promo period"),
|
||||
create(false, "reserved.tld", "Alloc token not in promo period"),
|
||||
create(false, "specificuse.tld", "Alloc token not in promo period"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_redeemedTokenOverridesOtherConcerns() throws Exception {
|
||||
setEppInput("domain_check_allocationtoken.xml");
|
||||
Domain domain = persistActiveDomain("example1.tld");
|
||||
HistoryEntryId historyEntryId = new HistoryEntryId(domain.getRepoId(), 1L);
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRedemptionHistoryId(historyEntryId)
|
||||
.build());
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "Alloc token was already redeemed"),
|
||||
create(false, "example2.tld", "Alloc token was already redeemed"),
|
||||
create(false, "reserved.tld", "Alloc token was already redeemed"),
|
||||
create(false, "specificuse.tld", "Alloc token was already redeemed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_allocationTokenPromotion_noPremium_stillPasses() throws Exception {
|
||||
createTld("example");
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
@@ -515,7 +503,7 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
ImmutableMap.of("DOMAIN", "rich.example"));
|
||||
doCheckTest(
|
||||
create(true, "example1.example", null),
|
||||
create(false, "rich.example", "Token not valid for premium name"),
|
||||
create(true, "rich.example", null),
|
||||
create(true, "example3.example", null));
|
||||
}
|
||||
|
||||
@@ -572,7 +560,8 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "Alloc token not in promo period"),
|
||||
create(false, "example2.example", "Alloc token not in promo period"),
|
||||
create(false, "reserved.tld", "Reserved"));
|
||||
create(false, "reserved.tld", "Alloc token not in promo period"),
|
||||
create(false, "rich.example", "Alloc token not in promo period"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -593,9 +582,10 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
.build());
|
||||
setEppInput("domain_check_allocationtoken_fee.xml");
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "Alloc token invalid for TLD"),
|
||||
create(true, "example1.tld", null),
|
||||
create(true, "example2.example", null),
|
||||
create(false, "reserved.tld", "Reserved"));
|
||||
create(false, "reserved.tld", "Reserved"),
|
||||
create(true, "rich.example", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -618,7 +608,8 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "Alloc token invalid for client"),
|
||||
create(false, "example2.example", "Alloc token invalid for client"),
|
||||
create(false, "reserved.tld", "Reserved"));
|
||||
create(false, "reserved.tld", "Alloc token invalid for client"),
|
||||
create(false, "rich.example", "Alloc token invalid for client"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -999,6 +990,7 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
.setToken("abc123")
|
||||
.setTokenType(UNLIMITED_USE)
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE, CommandName.TRANSFER))
|
||||
.setDiscountFraction(0.1)
|
||||
.build());
|
||||
setEppInput("domain_check_fee_multiple_commands_allocationtoken_v06.xml");
|
||||
runFlowAssertResponse(
|
||||
@@ -1028,6 +1020,7 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
.setToken("abc123")
|
||||
.setTokenType(UNLIMITED_USE)
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE, CommandName.TRANSFER))
|
||||
.setDiscountFraction(0.1)
|
||||
.build());
|
||||
setEppInput("domain_check_fee_multiple_commands_allocationtoken_v12.xml");
|
||||
runFlowAssertResponse(
|
||||
|
||||
@@ -141,13 +141,10 @@ import google.registry.flows.domain.DomainFlowUtils.TrailingDashException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.UnexpectedClaimsNoticeException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.UnsupportedMarkTypeException;
|
||||
import google.registry.flows.domain.DomainPricingLogic.AllocationTokenInvalidForPremiumNameException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.NonexistentAllocationTokenException;
|
||||
import google.registry.flows.exceptions.OnlyToolCanPassMetadataException;
|
||||
import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientException;
|
||||
import google.registry.flows.exceptions.ResourceCreateContentionException;
|
||||
@@ -529,51 +526,10 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
"domain_create_allocationtoken.xml",
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
|
||||
persistContactsAndHosts();
|
||||
EppException thrown = assertThrows(InvalidAllocationTokenException.class, this::runFlow);
|
||||
EppException thrown = assertThrows(NonexistentAllocationTokenException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_reservedDomainCreate_allocationTokenIsForADifferentDomain() {
|
||||
// Try to register a reserved domain name with an allocation token valid for a different domain
|
||||
// name.
|
||||
setEppInput(
|
||||
"domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "resdom.tld", "YEARS", "2"));
|
||||
persistContactsAndHosts();
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDomainName("otherdomain.tld")
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
EppException thrown =
|
||||
assertThrows(AllocationTokenNotValidForDomainException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
assertAllocationTokenWasNotRedeemed("abc123");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_nonreservedDomainCreate_allocationTokenIsForADifferentDomain() {
|
||||
// Try to register a non-reserved domain name with an allocation token valid for a different
|
||||
// domain name.
|
||||
setEppInput(
|
||||
"domain_create_allocationtoken.xml",
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
|
||||
persistContactsAndHosts();
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDomainName("otherdomain.tld")
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
EppException thrown =
|
||||
assertThrows(AllocationTokenNotValidForDomainException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
assertAllocationTokenWasNotRedeemed("abc123");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_alreadyRedemeedAllocationToken() {
|
||||
setEppInput(
|
||||
@@ -1704,32 +1660,6 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 204.44));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_promotionDoesNotApplyToPremiumPrice() {
|
||||
// Discounts only apply to premium domains if the token is explicitly configured to allow it.
|
||||
createTld("example");
|
||||
persistContactsAndHosts();
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(UNLIMITED_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||
.put(clock.nowUtc().plusMillis(1), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().plusSeconds(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
setEppInput(
|
||||
"domain_create_premium_allocationtoken.xml",
|
||||
ImmutableMap.of("YEARS", "2", "FEE", "193.50"));
|
||||
assertAboutEppExceptions()
|
||||
.that(assertThrows(AllocationTokenInvalidForPremiumNameException.class, this::runFlow))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_token_premiumDomainZeroPrice_noFeeExtension() throws Exception {
|
||||
createTld("example");
|
||||
@@ -1774,30 +1704,6 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_promoTokenNotValidForTld() {
|
||||
persistContactsAndHosts();
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(UNLIMITED_USE)
|
||||
.setAllowedTlds(ImmutableSet.of("example"))
|
||||
.setDiscountFraction(0.5)
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||
.put(clock.nowUtc().minusDays(1), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
setEppInput(
|
||||
"domain_create_allocationtoken.xml",
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
|
||||
assertAboutEppExceptions()
|
||||
.that(assertThrows(AllocationTokenNotValidForTldException.class, this::runFlow))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_promoTokenNotValidForRegistrar() {
|
||||
persistContactsAndHosts();
|
||||
@@ -3671,22 +3577,6 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertThrows(MissingClaimsNoticeException.class, this::runFlow);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_anchorTenant_mismatchedName_viaToken() throws Exception {
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
|
||||
.setDomainName("example.tld")
|
||||
.build());
|
||||
persistContactsAndHosts();
|
||||
setEppInput(
|
||||
"domain_create_allocationtoken.xml",
|
||||
ImmutableMap.of("DOMAIN", "example-one.tld", "YEARS", "2"));
|
||||
assertThrows(AllocationTokenNotValidForDomainException.class, this::runFlow);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_bulkToken_addsTokenToDomain() throws Exception {
|
||||
AllocationToken token =
|
||||
|
||||
@@ -179,7 +179,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
ImmutableMap<String, String> substitutions,
|
||||
boolean expectHistoryAndBilling)
|
||||
throws Exception {
|
||||
assertMutatingFlow(false);
|
||||
assertMutatingFlow(true);
|
||||
String expected =
|
||||
loadFile(expectedXmlFilename, updateSubstitutions(substitutions, "ROID", "2FF-TLD"));
|
||||
if (inactive) {
|
||||
|
||||
@@ -28,7 +28,6 @@ import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.JPY;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@@ -39,8 +38,6 @@ import google.registry.flows.EppException;
|
||||
import google.registry.flows.HttpSessionMetadata;
|
||||
import google.registry.flows.SessionMetadata;
|
||||
import google.registry.flows.custom.DomainPricingCustomLogic;
|
||||
import google.registry.flows.domain.DomainPricingLogic.AllocationTokenInvalidForCurrencyException;
|
||||
import google.registry.flows.domain.DomainPricingLogic.AllocationTokenInvalidForPremiumNameException;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
|
||||
import google.registry.model.billing.BillingRecurrence;
|
||||
@@ -214,32 +211,6 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void
|
||||
testGetDomainCreatePrice_withDiscountPriceToken_domainCurrencyDoesNotMatchTokensCurrency_throwsException() {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDiscountPrice(Money.of(JPY, new BigDecimal("250")))
|
||||
.setDiscountPremiums(false)
|
||||
.build());
|
||||
|
||||
// Domain's currency is not JPY (is USD).
|
||||
assertThrows(
|
||||
AllocationTokenInvalidForCurrencyException.class,
|
||||
() ->
|
||||
domainPricingLogic.getCreatePrice(
|
||||
tld,
|
||||
"default.example",
|
||||
clock.nowUtc(),
|
||||
3,
|
||||
false,
|
||||
false,
|
||||
Optional.of(allocationToken)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainRenewPrice_oneYear_standardDomain_noBilling_isStandardPrice()
|
||||
throws EppException {
|
||||
@@ -335,77 +306,6 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void
|
||||
testGetDomainRenewPrice_oneYear_premiumDomain_default_withTokenNotValidForPremiums_throwsException() {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountPremiums(false)
|
||||
.build());
|
||||
assertThrows(
|
||||
AllocationTokenInvalidForPremiumNameException.class,
|
||||
() ->
|
||||
domainPricingLogic.getRenewPrice(
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
persistDomainAndSetRecurrence("premium.example", DEFAULT, Optional.empty()),
|
||||
Optional.of(allocationToken)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void
|
||||
testGetDomainRenewPrice_oneYear_premiumDomain_default_withDiscountPriceToken_throwsException() {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDiscountPrice(Money.of(USD, 5))
|
||||
.setDiscountPremiums(false)
|
||||
.build());
|
||||
assertThrows(
|
||||
AllocationTokenInvalidForPremiumNameException.class,
|
||||
() ->
|
||||
domainPricingLogic.getRenewPrice(
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
persistDomainAndSetRecurrence("premium.example", DEFAULT, Optional.empty()),
|
||||
Optional.of(allocationToken)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void
|
||||
testGetDomainRenewPrice_withDiscountPriceToken_domainCurrencyDoesNotMatchTokensCurrency_throwsException() {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDiscountPrice(Money.of(JPY, new BigDecimal("250")))
|
||||
.setDiscountPremiums(false)
|
||||
.build());
|
||||
|
||||
// Domain's currency is not JPY (is USD).
|
||||
assertThrows(
|
||||
AllocationTokenInvalidForCurrencyException.class,
|
||||
() ->
|
||||
domainPricingLogic.getRenewPrice(
|
||||
tld,
|
||||
"default.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
persistDomainAndSetRecurrence("default.example", DEFAULT, Optional.empty()),
|
||||
Optional.of(allocationToken)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainRenewPrice_multiYear_premiumDomain_default_isPremiumCost() throws EppException {
|
||||
assertThat(
|
||||
@@ -450,30 +350,6 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void
|
||||
testGetDomainRenewPrice_multiYear_premiumDomain_default_withTokenNotValidForPremiums_throwsException() {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountPremiums(false)
|
||||
.setDiscountYears(2)
|
||||
.build());
|
||||
assertThrows(
|
||||
AllocationTokenInvalidForPremiumNameException.class,
|
||||
() ->
|
||||
domainPricingLogic.getRenewPrice(
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
persistDomainAndSetRecurrence("premium.example", DEFAULT, Optional.empty()),
|
||||
Optional.of(allocationToken)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainRenewPrice_oneYear_standardDomain_default_isNonPremiumPrice()
|
||||
throws EppException {
|
||||
|
||||
@@ -69,13 +69,12 @@ import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrarMustBeActiveForThisOperationException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException;
|
||||
import google.registry.flows.domain.DomainRenewFlow.IncorrectCurrentExpirationDateException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.MissingRemoveBulkPricingTokenOnBulkPricingDomainException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.NonexistentAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.RemoveBulkPricingTokenOnNonBulkPricingDomainException;
|
||||
import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException;
|
||||
import google.registry.model.billing.BillingBase.Flag;
|
||||
@@ -459,10 +458,14 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
ImmutableMap<String, String> customFeeMap =
|
||||
updateSubstitutions(
|
||||
FEE_06_MAP,
|
||||
"NAME", "costly-renew.tld",
|
||||
"PERIOD", "1",
|
||||
"EX_DATE", "2001-04-03T22:00:00.0Z",
|
||||
"FEE", "111.00");
|
||||
"NAME",
|
||||
"costly-renew.tld",
|
||||
"PERIOD",
|
||||
"1",
|
||||
"EX_DATE",
|
||||
"2001-04-03T22:00:00.0Z",
|
||||
"FEE",
|
||||
"111.00");
|
||||
setEppInput("domain_renew_fee.xml", customFeeMap);
|
||||
persistDomain();
|
||||
doSuccessfulTest(
|
||||
@@ -694,7 +697,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
"domain_renew_allocationtoken.xml",
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
|
||||
persistDomain();
|
||||
EppException thrown = assertThrows(InvalidAllocationTokenException.class, this::runFlow);
|
||||
EppException thrown = assertThrows(NonexistentAllocationTokenException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@@ -711,9 +714,12 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
.setDomainName("otherdomain.tld")
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
EppException thrown =
|
||||
assertThrows(AllocationTokenNotValidForDomainException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
assertAboutEppExceptions()
|
||||
.that(
|
||||
assertThrows(
|
||||
AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException.class,
|
||||
this::runFlow))
|
||||
.marshalsToXml();
|
||||
assertAllocationTokenWasNotRedeemed("abc123");
|
||||
}
|
||||
|
||||
@@ -784,9 +790,10 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
assertAboutEppExceptions()
|
||||
.that(assertThrows(AllocationTokenNotValidForTldException.class, this::runFlow))
|
||||
.marshalsToXml();
|
||||
runFlowAssertResponse(
|
||||
loadFile(
|
||||
"domain_renew_response.xml",
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2002-04-03T22:00:00.0Z")));
|
||||
assertAllocationTokenWasNotRedeemed("abc123");
|
||||
}
|
||||
|
||||
|
||||
@@ -52,12 +52,11 @@ import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
|
||||
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
|
||||
import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.NonexistentAllocationTokenException;
|
||||
import google.registry.flows.exceptions.NotPendingTransferException;
|
||||
import google.registry.model.billing.BillingBase;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
@@ -897,7 +896,7 @@ class DomainTransferApproveFlowTest
|
||||
@Test
|
||||
void testFailure_invalidAllocationToken() throws Exception {
|
||||
setEppInput("domain_transfer_approve_allocation_token.xml");
|
||||
EppException thrown = assertThrows(InvalidAllocationTokenException.class, this::runFlow);
|
||||
EppException thrown = assertThrows(NonexistentAllocationTokenException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@@ -910,9 +909,12 @@ class DomainTransferApproveFlowTest
|
||||
.setDomainName("otherdomain.tld")
|
||||
.build());
|
||||
setEppInput("domain_transfer_approve_allocation_token.xml");
|
||||
EppException thrown =
|
||||
assertThrows(AllocationTokenNotValidForDomainException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
assertAboutEppExceptions()
|
||||
.that(
|
||||
assertThrows(
|
||||
AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException.class,
|
||||
this::runFlow))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -971,8 +973,7 @@ class DomainTransferApproveFlowTest
|
||||
.build())
|
||||
.build());
|
||||
setEppInput("domain_transfer_approve_allocation_token.xml");
|
||||
EppException thrown = assertThrows(AllocationTokenNotValidForTldException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
runFlowAssertResponse(loadFile("domain_transfer_approve_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -79,11 +79,9 @@ import google.registry.flows.domain.DomainFlowUtils.PremiumNameBlockedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrarMustBeActiveForThisOperationException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.NonexistentAllocationTokenException;
|
||||
import google.registry.flows.exceptions.AlreadyPendingTransferException;
|
||||
import google.registry.flows.exceptions.InvalidTransferPeriodValueException;
|
||||
import google.registry.flows.exceptions.MissingTransferRequestAuthInfoException;
|
||||
@@ -505,7 +503,7 @@ class DomainTransferRequestFlowTest
|
||||
implicitTransferTime,
|
||||
transferCost,
|
||||
originalGracePeriods,
|
||||
/* expectTransferBillingEvent = */ true,
|
||||
/* expectTransferBillingEvent= */ true,
|
||||
extraExpectedBillingEvents);
|
||||
|
||||
assertPollMessagesEmitted(expectedExpirationTime, implicitTransferTime);
|
||||
@@ -1859,22 +1857,7 @@ class DomainTransferRequestFlowTest
|
||||
void testFailure_invalidAllocationToken() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
setEppInput("domain_transfer_request_allocation_token.xml", ImmutableMap.of("TOKEN", "abc123"));
|
||||
EppException thrown = assertThrows(InvalidAllocationTokenException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_allocationTokenIsForDifferentName() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDomainName("otherdomain.tld")
|
||||
.build());
|
||||
setEppInput("domain_transfer_request_allocation_token.xml", ImmutableMap.of("TOKEN", "abc123"));
|
||||
EppException thrown =
|
||||
assertThrows(AllocationTokenNotValidForDomainException.class, this::runFlow);
|
||||
EppException thrown = assertThrows(NonexistentAllocationTokenException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@@ -1920,27 +1903,6 @@ class DomainTransferRequestFlowTest
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_allocationTokenNotValidForTld() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(UNLIMITED_USE)
|
||||
.setAllowedTlds(ImmutableSet.of("example"))
|
||||
.setDiscountFraction(0.5)
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||
.put(clock.nowUtc().minusDays(1), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
setEppInput("domain_transfer_request_allocation_token.xml", ImmutableMap.of("TOKEN", "abc123"));
|
||||
EppException thrown = assertThrows(AllocationTokenNotValidForTldException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_allocationTokenAlreadyRedeemed() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
|
||||
+264
-267
@@ -19,31 +19,25 @@ 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.DEFAULT_PROMO;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForCommandException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainCommand;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.NonexistentAllocationTokenException;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
@@ -52,7 +46,7 @@ import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -62,319 +56,322 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
/** Unit tests for {@link AllocationTokenFlowUtils}. */
|
||||
class AllocationTokenFlowUtilsTest {
|
||||
|
||||
private final AllocationTokenFlowUtils flowUtils = new AllocationTokenFlowUtils();
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2025-01-10T01:00:00.000Z"));
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
|
||||
private final AllocationTokenExtension allocationTokenExtension =
|
||||
mock(AllocationTokenExtension.class);
|
||||
|
||||
private Tld tld;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
tld = createTld("tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateToken_successfullyVerifiesValidTokenOnCreate() throws Exception {
|
||||
void testSuccess_redeemsToken() {
|
||||
HistoryEntryId historyEntryId = new HistoryEntryId("repoId", 10L);
|
||||
assertThat(
|
||||
AllocationTokenFlowUtils.redeemToken(singleUseTokenBuilder().build(), historyEntryId)
|
||||
.getRedemptionHistoryId())
|
||||
.hasValue(historyEntryId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalidForPremiumName_validForPremium() {
|
||||
AllocationToken token = singleUseTokenBuilder().setDiscountPremiums(true).build();
|
||||
assertThat(AllocationTokenFlowUtils.discountTokenInvalidForPremiumName(token, true)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalidForPremiumName_notPremium() {
|
||||
assertThat(
|
||||
AllocationTokenFlowUtils.discountTokenInvalidForPremiumName(
|
||||
singleUseTokenBuilder().build(), false))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalidForPremiumName_invalidForPremium() {
|
||||
assertThat(
|
||||
AllocationTokenFlowUtils.discountTokenInvalidForPremiumName(
|
||||
singleUseTokenBuilder().build(), true))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_loadFromExtension() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("tokeN")
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE, CommandName.RESTORE))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setTokenType(SINGLE_USE)
|
||||
.build());
|
||||
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
|
||||
assertThat(
|
||||
flowUtils
|
||||
.verifyAllocationTokenCreateIfPresent(
|
||||
createCommand("blah.tld"),
|
||||
Tld.get("tld"),
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC),
|
||||
Optional.of(allocationTokenExtension))
|
||||
.get())
|
||||
.isEqualTo(token);
|
||||
AllocationTokenFlowUtils.loadAllocationTokenFromExtension(
|
||||
"TheRegistrar",
|
||||
"example.tld",
|
||||
clock.nowUtc(),
|
||||
Optional.of(allocationTokenExtension)))
|
||||
.hasValue(token);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateToken_successfullyVerifiesValidTokenExistingDomain() throws Exception {
|
||||
void testSuccess_loadOrDefault_fromExtensionEvenWhenDefaultPresent() throws Exception {
|
||||
persistDefaultToken();
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("tokeN")
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE, CommandName.RENEW))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setTokenType(SINGLE_USE)
|
||||
.build());
|
||||
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
|
||||
assertThat(
|
||||
flowUtils
|
||||
.verifyAllocationTokenIfPresent(
|
||||
DatabaseHelper.newDomain("blah.tld"),
|
||||
Tld.get("tld"),
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC),
|
||||
CommandName.RENEW,
|
||||
Optional.of(allocationTokenExtension))
|
||||
.get())
|
||||
.isEqualTo(token);
|
||||
AllocationTokenFlowUtils.loadTokenFromExtensionOrGetDefault(
|
||||
"TheRegistrar",
|
||||
clock.nowUtc(),
|
||||
Optional.of(allocationTokenExtension),
|
||||
tld,
|
||||
"example.tld",
|
||||
CommandName.CREATE))
|
||||
.hasValue(token);
|
||||
}
|
||||
|
||||
void test_validateToken_emptyAllowedEppActions_successfullyVerifiesValidTokenExistingDomain()
|
||||
throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
|
||||
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
|
||||
@Test
|
||||
void testSuccess_loadOrDefault_defaultWhenNonePresent() throws Exception {
|
||||
AllocationToken defaultToken = persistDefaultToken();
|
||||
assertThat(
|
||||
flowUtils
|
||||
.verifyAllocationTokenIfPresent(
|
||||
DatabaseHelper.newDomain("blah.tld"),
|
||||
Tld.get("tld"),
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC),
|
||||
CommandName.RENEW,
|
||||
Optional.of(allocationTokenExtension))
|
||||
.get())
|
||||
.isEqualTo(token);
|
||||
AllocationTokenFlowUtils.loadTokenFromExtensionOrGetDefault(
|
||||
"TheRegistrar",
|
||||
clock.nowUtc(),
|
||||
Optional.empty(),
|
||||
tld,
|
||||
"example.tld",
|
||||
CommandName.CREATE))
|
||||
.hasValue(defaultToken);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateTokenCreate_failsOnNonexistentToken() {
|
||||
assertValidateCreateThrowsEppException(InvalidAllocationTokenException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateTokenExistingDomain_failsOnNonexistentToken() {
|
||||
assertValidateExistingDomainThrowsEppException(InvalidAllocationTokenException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateTokenCreate_failsOnNullToken() {
|
||||
assertAboutEppExceptions()
|
||||
.that(
|
||||
assertThrows(
|
||||
InvalidAllocationTokenException.class,
|
||||
() ->
|
||||
flowUtils.verifyAllocationTokenCreateIfPresent(
|
||||
createCommand("blah.tld"),
|
||||
Tld.get("tld"),
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC),
|
||||
Optional.of(allocationTokenExtension))))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateTokenExistingDomain_failsOnNullToken() {
|
||||
assertAboutEppExceptions()
|
||||
.that(
|
||||
assertThrows(
|
||||
InvalidAllocationTokenException.class,
|
||||
() ->
|
||||
flowUtils.verifyAllocationTokenIfPresent(
|
||||
DatabaseHelper.newDomain("blah.tld"),
|
||||
Tld.get("tld"),
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC),
|
||||
CommandName.RENEW,
|
||||
Optional.of(allocationTokenExtension))))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateTokenCreate_invalidForClientId() {
|
||||
persistResource(
|
||||
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.build());
|
||||
assertValidateCreateThrowsEppException(AllocationTokenNotValidForRegistrarException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateTokenExistingDomain_invalidForClientId() {
|
||||
persistResource(
|
||||
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.build());
|
||||
assertValidateExistingDomainThrowsEppException(
|
||||
AllocationTokenNotValidForRegistrarException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateTokenCreate_invalidForTld() {
|
||||
persistResource(
|
||||
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||
.setAllowedTlds(ImmutableSet.of("nottld"))
|
||||
.build());
|
||||
assertValidateCreateThrowsEppException(AllocationTokenNotValidForTldException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateTokenExistingDomain_invalidForTld() {
|
||||
persistResource(
|
||||
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||
.setAllowedTlds(ImmutableSet.of("nottld"))
|
||||
.build());
|
||||
assertValidateExistingDomainThrowsEppException(AllocationTokenNotValidForTldException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateTokenCreate_beforePromoStart() {
|
||||
persistResource(createOneMonthPromoTokenBuilder(DateTime.now(UTC).plusDays(1)).build());
|
||||
assertValidateCreateThrowsEppException(AllocationTokenNotInPromotionException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateTokenExistingDomain_beforePromoStart() {
|
||||
persistResource(createOneMonthPromoTokenBuilder(DateTime.now(UTC).plusDays(1)).build());
|
||||
assertValidateExistingDomainThrowsEppException(AllocationTokenNotInPromotionException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateTokenCreate_afterPromoEnd() {
|
||||
persistResource(createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusMonths(2)).build());
|
||||
assertValidateCreateThrowsEppException(AllocationTokenNotInPromotionException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateTokenExistingDomain_afterPromoEnd() {
|
||||
persistResource(createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusMonths(2)).build());
|
||||
assertValidateExistingDomainThrowsEppException(AllocationTokenNotInPromotionException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateTokenCreate_promoCancelled() {
|
||||
// the promo would be valid, but it was cancelled 12 hours ago
|
||||
persistResource(
|
||||
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, NOT_STARTED)
|
||||
.put(DateTime.now(UTC).minusMonths(1), VALID)
|
||||
.put(DateTime.now(UTC).minusHours(12), CANCELLED)
|
||||
.build())
|
||||
.build());
|
||||
assertValidateCreateThrowsEppException(AllocationTokenNotInPromotionException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateTokenExistingDomain_promoCancelled() {
|
||||
// the promo would be valid, but it was cancelled 12 hours ago
|
||||
persistResource(
|
||||
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, NOT_STARTED)
|
||||
.put(DateTime.now(UTC).minusMonths(1), VALID)
|
||||
.put(DateTime.now(UTC).minusHours(12), CANCELLED)
|
||||
.build())
|
||||
.build());
|
||||
assertValidateExistingDomainThrowsEppException(AllocationTokenNotInPromotionException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateTokenCreate_invalidCommand() {
|
||||
persistResource(
|
||||
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.RENEW))
|
||||
.build());
|
||||
assertValidateCreateThrowsEppException(AllocationTokenNotValidForCommandException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_validateTokenExistingDomain_invalidCommand() {
|
||||
persistResource(
|
||||
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.build());
|
||||
assertValidateExistingDomainThrowsEppException(
|
||||
AllocationTokenNotValidForCommandException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_checkDomainsWithToken_successfullyVerifiesValidToken() {
|
||||
persistResource(
|
||||
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
|
||||
assertThat(
|
||||
flowUtils
|
||||
.checkDomainsWithToken(
|
||||
ImmutableList.of(
|
||||
InternetDomainName.from("blah.tld"), InternetDomainName.from("blah2.tld")),
|
||||
"tokeN",
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC))
|
||||
.domainCheckResults())
|
||||
.containsExactlyEntriesIn(
|
||||
ImmutableMap.of(
|
||||
InternetDomainName.from("blah.tld"), "", InternetDomainName.from("blah2.tld"), ""))
|
||||
.inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_checkDomainsWithToken_showsFailureMessageForRedeemedToken() {
|
||||
Domain domain = persistActiveDomain("example.tld");
|
||||
HistoryEntryId historyEntryId = new HistoryEntryId(domain.getRepoId(), 1051L);
|
||||
void testSuccess_loadOrDefault_defaultWhenTokenIsPresentButNotApplicable() throws Exception {
|
||||
AllocationToken defaultToken = persistDefaultToken();
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("tokeN")
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRedemptionHistoryId(historyEntryId)
|
||||
.setAllowedTlds(ImmutableSet.of("othertld"))
|
||||
.build());
|
||||
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
|
||||
assertThat(
|
||||
flowUtils
|
||||
.checkDomainsWithToken(
|
||||
ImmutableList.of(
|
||||
InternetDomainName.from("blah.tld"), InternetDomainName.from("blah2.tld")),
|
||||
"tokeN",
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC))
|
||||
.domainCheckResults())
|
||||
.containsExactlyEntriesIn(
|
||||
ImmutableMap.of(
|
||||
InternetDomainName.from("blah.tld"),
|
||||
"Alloc token was already redeemed",
|
||||
InternetDomainName.from("blah2.tld"),
|
||||
"Alloc token was already redeemed"))
|
||||
.inOrder();
|
||||
AllocationTokenFlowUtils.loadTokenFromExtensionOrGetDefault(
|
||||
"TheRegistrar",
|
||||
clock.nowUtc(),
|
||||
Optional.of(allocationTokenExtension),
|
||||
tld,
|
||||
"example.tld",
|
||||
CommandName.CREATE))
|
||||
.hasValue(defaultToken);
|
||||
}
|
||||
|
||||
private void assertValidateCreateThrowsEppException(Class<? extends EppException> clazz) {
|
||||
@Test
|
||||
void testValidAgainstDomain_validAllReasons() {
|
||||
AllocationToken token = singleUseTokenBuilder().setDiscountPremiums(true).build();
|
||||
assertThat(
|
||||
AllocationTokenFlowUtils.tokenIsValidAgainstDomain(
|
||||
InternetDomainName.from("rich.tld"), token, CommandName.CREATE, clock.nowUtc()))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidAgainstDomain_invalidPremium() {
|
||||
AllocationToken token = singleUseTokenBuilder().build();
|
||||
assertThat(
|
||||
AllocationTokenFlowUtils.tokenIsValidAgainstDomain(
|
||||
InternetDomainName.from("rich.tld"), token, CommandName.CREATE, clock.nowUtc()))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidAgainstDomain_invalidAction() {
|
||||
AllocationToken token =
|
||||
singleUseTokenBuilder().setAllowedEppActions(ImmutableSet.of(CommandName.RESTORE)).build();
|
||||
assertThat(
|
||||
AllocationTokenFlowUtils.tokenIsValidAgainstDomain(
|
||||
InternetDomainName.from("domain.tld"), token, CommandName.CREATE, clock.nowUtc()))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidAgainstDomain_invalidTld() {
|
||||
createTld("othertld");
|
||||
AllocationToken token = singleUseTokenBuilder().build();
|
||||
assertThat(
|
||||
AllocationTokenFlowUtils.tokenIsValidAgainstDomain(
|
||||
InternetDomainName.from("domain.othertld"),
|
||||
token,
|
||||
CommandName.CREATE,
|
||||
clock.nowUtc()))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidAgainstDomain_invalidDomain() {
|
||||
AllocationToken token = singleUseTokenBuilder().setDomainName("anchor.tld").build();
|
||||
assertThat(
|
||||
AllocationTokenFlowUtils.tokenIsValidAgainstDomain(
|
||||
InternetDomainName.from("domain.tld"), token, CommandName.CREATE, clock.nowUtc()))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_redeemToken_nonSingleUse() {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
AllocationTokenFlowUtils.redeemToken(
|
||||
createOneMonthPromoTokenBuilder(clock.nowUtc()).build(),
|
||||
new HistoryEntryId("repoId", 10L)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_loadFromExtension_nonexistentToken() {
|
||||
assertLoadTokenFromExtensionThrowsException(NonexistentAllocationTokenException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_loadFromExtension_nullToken() {
|
||||
when(allocationTokenExtension.getAllocationToken()).thenReturn(null);
|
||||
assertLoadTokenFromExtensionThrowsException(NonexistentAllocationTokenException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_tokenInvalidForRegistrar() {
|
||||
persistResource(
|
||||
createOneMonthPromoTokenBuilder(clock.nowUtc().minusDays(1))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.build());
|
||||
assertLoadTokenFromExtensionThrowsException(AllocationTokenNotValidForRegistrarException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_beforePromoStart() {
|
||||
persistResource(createOneMonthPromoTokenBuilder(clock.nowUtc().plusDays(1)).build());
|
||||
assertLoadTokenFromExtensionThrowsException(AllocationTokenNotInPromotionException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_afterPromoEnd() {
|
||||
persistResource(createOneMonthPromoTokenBuilder(clock.nowUtc().minusMonths(2)).build());
|
||||
assertLoadTokenFromExtensionThrowsException(AllocationTokenNotInPromotionException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_promoCancelled() {
|
||||
// the promo would be valid, but it was cancelled 12 hours ago
|
||||
persistResource(
|
||||
createOneMonthPromoTokenBuilder(clock.nowUtc().minusDays(1))
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, NOT_STARTED)
|
||||
.put(clock.nowUtc().minusMonths(1), VALID)
|
||||
.put(clock.nowUtc().minusHours(12), CANCELLED)
|
||||
.build())
|
||||
.build());
|
||||
assertLoadTokenFromExtensionThrowsException(AllocationTokenNotInPromotionException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_loadOrDefault_badTokenProvided() throws Exception {
|
||||
when(allocationTokenExtension.getAllocationToken()).thenReturn("asdf");
|
||||
assertThrows(
|
||||
NonexistentAllocationTokenException.class,
|
||||
() ->
|
||||
AllocationTokenFlowUtils.loadTokenFromExtensionOrGetDefault(
|
||||
"TheRegistrar",
|
||||
clock.nowUtc(),
|
||||
Optional.of(allocationTokenExtension),
|
||||
tld,
|
||||
"example.tld",
|
||||
CommandName.CREATE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_loadOrDefault_noValidTokens() throws Exception {
|
||||
assertThat(
|
||||
AllocationTokenFlowUtils.loadTokenFromExtensionOrGetDefault(
|
||||
"TheRegistrar",
|
||||
clock.nowUtc(),
|
||||
Optional.empty(),
|
||||
tld,
|
||||
"example.tld",
|
||||
CommandName.CREATE))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_loadOrDefault_badDomainName() throws Exception {
|
||||
// Tokens tied to a domain should throw a catastrophic exception if used for a different domain
|
||||
persistResource(singleUseTokenBuilder().setDomainName("someotherdomain.tld").build());
|
||||
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
|
||||
assertThrows(
|
||||
AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException.class,
|
||||
() ->
|
||||
AllocationTokenFlowUtils.loadTokenFromExtensionOrGetDefault(
|
||||
"TheRegistrar",
|
||||
clock.nowUtc(),
|
||||
Optional.of(allocationTokenExtension),
|
||||
tld,
|
||||
"example.tld",
|
||||
CommandName.CREATE));
|
||||
}
|
||||
|
||||
private AllocationToken persistDefaultToken() {
|
||||
AllocationToken defaultToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("defaultToken")
|
||||
.setDiscountFraction(0.1)
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setTokenType(DEFAULT_PROMO)
|
||||
.build());
|
||||
tld =
|
||||
persistResource(
|
||||
tld.asBuilder()
|
||||
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
|
||||
.build());
|
||||
return defaultToken;
|
||||
}
|
||||
|
||||
private void assertLoadTokenFromExtensionThrowsException(Class<? extends EppException> clazz) {
|
||||
assertAboutEppExceptions()
|
||||
.that(
|
||||
assertThrows(
|
||||
clazz,
|
||||
() ->
|
||||
flowUtils.verifyAllocationTokenCreateIfPresent(
|
||||
createCommand("blah.tld"),
|
||||
Tld.get("tld"),
|
||||
AllocationTokenFlowUtils.loadAllocationTokenFromExtension(
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC),
|
||||
"example.tld",
|
||||
clock.nowUtc(),
|
||||
Optional.of(allocationTokenExtension))))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
private void assertValidateExistingDomainThrowsEppException(Class<? extends EppException> clazz) {
|
||||
assertAboutEppExceptions()
|
||||
.that(
|
||||
assertThrows(
|
||||
clazz,
|
||||
() ->
|
||||
flowUtils.verifyAllocationTokenIfPresent(
|
||||
DatabaseHelper.newDomain("blah.tld"),
|
||||
Tld.get("tld"),
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC),
|
||||
CommandName.RENEW,
|
||||
Optional.of(allocationTokenExtension))))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
private static DomainCommand.Create createCommand(String domainName) {
|
||||
DomainCommand.Create command = mock(DomainCommand.Create.class);
|
||||
when(command.getDomainName()).thenReturn(domainName);
|
||||
return command;
|
||||
private AllocationToken.Builder singleUseTokenBuilder() {
|
||||
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
|
||||
return new AllocationToken.Builder()
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setToken("tokeN")
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setDiscountFraction(0.1)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"));
|
||||
}
|
||||
|
||||
private AllocationToken.Builder createOneMonthPromoTokenBuilder(DateTime promoStart) {
|
||||
|
||||
@@ -496,6 +496,28 @@ class RegistrarTest extends EntityTestCase {
|
||||
.isEqualTo(fakeClock.nowUtc());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_setLastPocVerificationDate() {
|
||||
assertThat(
|
||||
registrar
|
||||
.asBuilder()
|
||||
.setLastPocVerificationDate(fakeClock.nowUtc())
|
||||
.build()
|
||||
.getLastPocVerificationDate())
|
||||
.isEqualTo(fakeClock.nowUtc());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_setLastPocVerificationDate_nullDate() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> new Registrar.Builder().setLastPocVerificationDate(null).build());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Registrar lastPocVerificationDate cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_setLastExpiringFailoverCertNotificationSentDate_nullDate() {
|
||||
IllegalArgumentException thrown =
|
||||
|
||||
@@ -15,16 +15,21 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.JPY;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.beust.jcommander.ParameterException;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.dns.writer.VoidDnsWriter;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.pricing.StaticPremiumListPricingEngine;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
@@ -111,12 +116,15 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
|
||||
|
||||
@Test
|
||||
void testSuccess_minimal() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag()
|
||||
.asBuilder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, ACTIVE))
|
||||
.build());
|
||||
// Test that each optional field can be omitted. Also tests the auto-gen password.
|
||||
runCommandForced(
|
||||
"--client=NewRegistrar",
|
||||
"--registrant=crr-admin",
|
||||
"--admins=crr-admin",
|
||||
"--techs=crr-tech",
|
||||
"example.tld");
|
||||
eppVerifier.verifySent("domain_create_minimal.xml");
|
||||
}
|
||||
@@ -131,7 +139,9 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
|
||||
"--techs=crr-tech",
|
||||
"example.tld",
|
||||
"example.abc");
|
||||
eppVerifier.verifySent("domain_create_minimal.xml").verifySent("domain_create_minimal_abc.xml");
|
||||
eppVerifier
|
||||
.verifySent("domain_create_contacts.xml")
|
||||
.verifySent("domain_create_contacts_abc.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -152,8 +162,8 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
|
||||
"example.tld",
|
||||
"example.abc");
|
||||
eppVerifier
|
||||
.verifySent("domain_create_minimal.xml")
|
||||
.verifySent("domain_create_minimal_abc.xml");
|
||||
.verifySent("domain_create_contacts.xml")
|
||||
.verifySent("domain_create_contacts_abc.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -192,9 +202,9 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
|
||||
"palladium.tld",
|
||||
"example.abc");
|
||||
eppVerifier
|
||||
.verifySent("domain_create_minimal.xml")
|
||||
.verifySent("domain_create_contacts.xml")
|
||||
.verifySent("domain_create_palladium.xml")
|
||||
.verifySent("domain_create_minimal_abc.xml");
|
||||
.verifySent("domain_create_contacts_abc.xml");
|
||||
assertInStdout(
|
||||
"palladium.tld is premium at USD 877.00 per year; "
|
||||
+ "sending total cost for 1 year(s) of USD 877.00.");
|
||||
@@ -227,6 +237,19 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
|
||||
eppVerifier.verifySent("domain_create_token.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_contactsStillRequired() throws Exception {
|
||||
// Verify that if contacts are still required, the minimum+contacts request is sent
|
||||
createTld("tld");
|
||||
runCommandForced(
|
||||
"--client=NewRegistrar",
|
||||
"--registrant=crr-admin",
|
||||
"--admins=crr-admin",
|
||||
"--techs=crr-tech",
|
||||
"example.tld");
|
||||
eppVerifier.verifySent("domain_create_contacts.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_duplicateDomains() {
|
||||
IllegalArgumentException thrown =
|
||||
|
||||
+71
-11
@@ -40,6 +40,7 @@ import google.registry.request.Action;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.testing.ConsoleApiParamsUtils;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.SystemPropertyExtension;
|
||||
import google.registry.tools.GsonUtils;
|
||||
@@ -51,6 +52,7 @@ import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -59,7 +61,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
/** Tests for {@link google.registry.ui.server.console.ConsoleUpdateRegistrarAction}. */
|
||||
class ConsoleUpdateRegistrarActionTest {
|
||||
private static final Gson GSON = GsonUtils.provideGson();
|
||||
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2025-01-01T00:00:00.000Z"));
|
||||
private ConsoleApiParams consoleApiParams;
|
||||
private FakeResponse response;
|
||||
|
||||
@@ -68,12 +70,17 @@ class ConsoleUpdateRegistrarActionTest {
|
||||
private User user;
|
||||
|
||||
private static String registrarPostData =
|
||||
"{\"registrarId\":\"%s\",\"allowedTlds\":[%s],\"registryLockAllowed\":%s}";
|
||||
"{\"registrarId\":\"%s\",\"allowedTlds\":[%s],\"registryLockAllowed\":%s,"
|
||||
+ " \"lastPocVerificationDate\":%s }";
|
||||
|
||||
@RegisterExtension
|
||||
@Order(Integer.MAX_VALUE)
|
||||
final SystemPropertyExtension systemPropertyExtension = new SystemPropertyExtension();
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
createTlds("app", "dev");
|
||||
@@ -94,13 +101,16 @@ class ConsoleUpdateRegistrarActionTest {
|
||||
consoleApiParams = createParams();
|
||||
}
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
@Test
|
||||
void testSuccess_updatesRegistrar() throws IOException {
|
||||
var action = createAction(String.format(registrarPostData, "TheRegistrar", "app, dev", false));
|
||||
var action =
|
||||
createAction(
|
||||
String.format(
|
||||
registrarPostData,
|
||||
"TheRegistrar",
|
||||
"app, dev",
|
||||
false,
|
||||
"\"2024-12-12T00:00:00.000Z\""));
|
||||
action.run();
|
||||
Registrar newRegistrar = Registrar.loadByRegistrarId("TheRegistrar").get();
|
||||
assertThat(newRegistrar.getAllowedTlds()).containsExactly("app", "dev");
|
||||
@@ -111,10 +121,44 @@ class ConsoleUpdateRegistrarActionTest {
|
||||
assertThat(history.getDescription()).hasValue("TheRegistrar");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_updatesNullPocVerificationDate() throws IOException {
|
||||
var action =
|
||||
createAction(
|
||||
String.format(registrarPostData, "TheRegistrar", "app, dev", false, "\"null\""));
|
||||
action.run();
|
||||
Registrar newRegistrar = Registrar.loadByRegistrarId("TheRegistrar").get();
|
||||
assertThat(newRegistrar.getLastPocVerificationDate())
|
||||
.isEqualTo(DateTime.parse("1970-01-01T00:00:00.000Z"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_pocVerificationInTheFuture() throws IOException {
|
||||
var action =
|
||||
createAction(
|
||||
String.format(
|
||||
registrarPostData,
|
||||
"TheRegistrar",
|
||||
"app, dev",
|
||||
false,
|
||||
"\"2025-02-01T00:00:00.000Z\""));
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat((String) ((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
.contains("Invalid value of LastPocVerificationDate - value is in the future");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFails_missingWhoisContact() throws IOException {
|
||||
RegistryEnvironment.PRODUCTION.setup(systemPropertyExtension);
|
||||
var action = createAction(String.format(registrarPostData, "TheRegistrar", "app, dev", false));
|
||||
var action =
|
||||
createAction(
|
||||
String.format(
|
||||
registrarPostData,
|
||||
"TheRegistrar",
|
||||
"app, dev",
|
||||
false,
|
||||
"\"2024-12-12T00:00:00.000Z\""));
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat((String) ((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
@@ -137,7 +181,14 @@ class ConsoleUpdateRegistrarActionTest {
|
||||
.setVisibleInDomainWhoisAsAbuse(true)
|
||||
.build();
|
||||
persistResource(contact);
|
||||
var action = createAction(String.format(registrarPostData, "TheRegistrar", "app, dev", false));
|
||||
var action =
|
||||
createAction(
|
||||
String.format(
|
||||
registrarPostData,
|
||||
"TheRegistrar",
|
||||
"app, dev",
|
||||
false,
|
||||
"\"2024-12-12T00:00:00.000Z\""));
|
||||
action.run();
|
||||
Registrar newRegistrar = Registrar.loadByRegistrarId("TheRegistrar").get();
|
||||
assertThat(newRegistrar.getAllowedTlds()).containsExactly("app", "dev");
|
||||
@@ -147,7 +198,14 @@ class ConsoleUpdateRegistrarActionTest {
|
||||
|
||||
@Test
|
||||
void testSuccess_sendsEmail() throws AddressException, IOException {
|
||||
var action = createAction(String.format(registrarPostData, "TheRegistrar", "app, dev", false));
|
||||
var action =
|
||||
createAction(
|
||||
String.format(
|
||||
registrarPostData,
|
||||
"TheRegistrar",
|
||||
"app, dev",
|
||||
false,
|
||||
"\"2024-12-12T00:00:00.000Z\""));
|
||||
action.run();
|
||||
verify(consoleApiParams.sendEmailUtils().gmailClient, times(1))
|
||||
.sendEmail(
|
||||
@@ -159,7 +217,9 @@ class ConsoleUpdateRegistrarActionTest {
|
||||
"The following changes were made in registry unittest environment to the"
|
||||
+ " registrar TheRegistrar by user user@registrarId.com:\n"
|
||||
+ "\n"
|
||||
+ "allowedTlds: null -> [app, dev]\n")
|
||||
+ "allowedTlds: null -> [app, dev]\n"
|
||||
+ "lastPocVerificationDate: 1970-01-01T00:00:00.000Z ->"
|
||||
+ " 2024-12-12T00:00:00.000Z\n")
|
||||
.setRecipients(ImmutableList.of(new InternetAddress("notification@test.example")))
|
||||
.build());
|
||||
}
|
||||
|
||||
+95
-74
@@ -18,6 +18,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.registrar.RegistrarPoc.Type.ABUSE;
|
||||
import static google.registry.model.registrar.RegistrarPoc.Type.ADMIN;
|
||||
import static google.registry.model.registrar.RegistrarPoc.Type.MARKETING;
|
||||
import static google.registry.model.registrar.RegistrarPoc.Type.TECH;
|
||||
import static google.registry.testing.DatabaseHelper.createAdminUser;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
@@ -70,8 +71,9 @@ class ContactActionTest {
|
||||
|
||||
private Registrar testRegistrar;
|
||||
private ConsoleApiParams consoleApiParams;
|
||||
private RegistrarPoc testRegistrarPoc1;
|
||||
private RegistrarPoc testRegistrarPoc2;
|
||||
private RegistrarPoc adminPoc;
|
||||
private RegistrarPoc techPoc;
|
||||
private RegistrarPoc marketingPoc;
|
||||
|
||||
private static final Gson GSON = RequestModule.provideGson();
|
||||
|
||||
@@ -82,7 +84,7 @@ class ContactActionTest {
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
testRegistrar = saveRegistrar("registrarId");
|
||||
testRegistrarPoc1 =
|
||||
adminPoc =
|
||||
new RegistrarPoc.Builder()
|
||||
.setRegistrar(testRegistrar)
|
||||
.setName("Test Registrar 1")
|
||||
@@ -94,19 +96,32 @@ class ContactActionTest {
|
||||
.setVisibleInWhoisAsTech(false)
|
||||
.setVisibleInDomainWhoisAsAbuse(false)
|
||||
.build();
|
||||
testRegistrarPoc2 =
|
||||
testRegistrarPoc1
|
||||
techPoc =
|
||||
adminPoc
|
||||
.asBuilder()
|
||||
.setName("Test Registrar 2")
|
||||
.setTypes(ImmutableSet.of(TECH))
|
||||
.setVisibleInWhoisAsTech(true)
|
||||
.setVisibleInWhoisAsAdmin(false)
|
||||
.setEmailAddress("test.registrar2@example.com")
|
||||
.setPhoneNumber("+1.1234567890")
|
||||
.setFaxNumber("+1.1234567891")
|
||||
.build();
|
||||
marketingPoc =
|
||||
adminPoc
|
||||
.asBuilder()
|
||||
.setName("Test Registrar 3")
|
||||
.setTypes(ImmutableSet.of(MARKETING))
|
||||
.setVisibleInWhoisAsAdmin(false)
|
||||
.setEmailAddress("test.registrar3@example.com")
|
||||
.setPhoneNumber("+1.1238675309")
|
||||
.setFaxNumber("+1.1238675309")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_getContactInfo() throws IOException {
|
||||
insertInDb(testRegistrarPoc1);
|
||||
insertInDb(adminPoc);
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.GET,
|
||||
@@ -120,13 +135,13 @@ class ContactActionTest {
|
||||
|
||||
@Test
|
||||
void testSuccess_noOp() throws IOException {
|
||||
insertInDb(testRegistrarPoc1);
|
||||
insertInDb(adminPoc);
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.createUser(createAdminUser("email@email.com")),
|
||||
testRegistrar.getRegistrarId(),
|
||||
testRegistrarPoc1);
|
||||
adminPoc);
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
|
||||
verify(consoleApiParams.sendEmailUtils().gmailClient, never()).sendEmail(any());
|
||||
@@ -134,8 +149,8 @@ class ContactActionTest {
|
||||
|
||||
@Test
|
||||
void testSuccess_onlyContactsWithNonEmptyType() throws IOException {
|
||||
testRegistrarPoc1 = testRegistrarPoc1.asBuilder().setTypes(ImmutableSet.of()).build();
|
||||
insertInDb(testRegistrarPoc1);
|
||||
adminPoc = adminPoc.asBuilder().setTypes(ImmutableSet.of()).build();
|
||||
insertInDb(adminPoc);
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.GET,
|
||||
@@ -148,14 +163,14 @@ class ContactActionTest {
|
||||
|
||||
@Test
|
||||
void testSuccess_postCreateContactInfo() throws IOException {
|
||||
insertInDb(testRegistrarPoc1);
|
||||
insertInDb(adminPoc);
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.createUser(createAdminUser("email@email.com")),
|
||||
testRegistrar.getRegistrarId(),
|
||||
testRegistrarPoc1,
|
||||
testRegistrarPoc2);
|
||||
adminPoc,
|
||||
techPoc);
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(
|
||||
@@ -168,14 +183,14 @@ class ContactActionTest {
|
||||
|
||||
@Test
|
||||
void testSuccess_postUpdateContactInfo() throws IOException {
|
||||
insertInDb(testRegistrarPoc1.asBuilder().setEmailAddress("incorrect@email.com").build());
|
||||
insertInDb(techPoc.asBuilder().setEmailAddress("incorrect@email.com").build());
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.createUser(createAdminUser("email@email.com")),
|
||||
testRegistrar.getRegistrarId(),
|
||||
testRegistrarPoc1,
|
||||
testRegistrarPoc2);
|
||||
adminPoc,
|
||||
techPoc);
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
|
||||
HashMap<String, String> testResult = new HashMap<>();
|
||||
@@ -197,8 +212,8 @@ class ContactActionTest {
|
||||
Action.Method.POST,
|
||||
AuthResult.createUser(createAdminUser("email@email.com")),
|
||||
testRegistrar.getRegistrarId(),
|
||||
testRegistrarPoc1,
|
||||
testRegistrarPoc2.asBuilder().setEmailAddress("test.registrar1@example.com").build());
|
||||
adminPoc,
|
||||
techPoc.asBuilder().setEmailAddress("test.registrar1@example.com").build());
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
@@ -213,13 +228,13 @@ class ContactActionTest {
|
||||
|
||||
@Test
|
||||
void testFailure_postUpdateContactInfo_requiredContactRemoved() throws IOException {
|
||||
insertInDb(testRegistrarPoc1);
|
||||
insertInDb(adminPoc);
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.createUser(createAdminUser("email@email.com")),
|
||||
testRegistrar.getRegistrarId(),
|
||||
testRegistrarPoc1.asBuilder().setTypes(ImmutableSet.of(ABUSE)).build());
|
||||
adminPoc.asBuilder().setTypes(ImmutableSet.of(ABUSE)).build());
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
@@ -228,20 +243,19 @@ class ContactActionTest {
|
||||
loadAllOf(RegistrarPoc.class).stream()
|
||||
.filter(r -> r.registrarId.equals(testRegistrar.getRegistrarId()))
|
||||
.collect(toImmutableList()))
|
||||
.containsExactly(testRegistrarPoc1);
|
||||
.containsExactly(adminPoc);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_postUpdateContactInfo_phoneNumberRemoved() throws IOException {
|
||||
testRegistrarPoc1 =
|
||||
testRegistrarPoc1.asBuilder().setTypes(ImmutableSet.of(ADMIN, TECH)).build();
|
||||
insertInDb(testRegistrarPoc1);
|
||||
adminPoc = adminPoc.asBuilder().setTypes(ImmutableSet.of(ADMIN, TECH)).build();
|
||||
insertInDb(adminPoc);
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.createUser(createAdminUser("email@email.com")),
|
||||
testRegistrar.getRegistrarId(),
|
||||
testRegistrarPoc1
|
||||
adminPoc
|
||||
.asBuilder()
|
||||
.setPhoneNumber(null)
|
||||
.setTypes(ImmutableSet.of(ADMIN, TECH))
|
||||
@@ -254,7 +268,7 @@ class ContactActionTest {
|
||||
loadAllOf(RegistrarPoc.class).stream()
|
||||
.filter(r -> r.registrarId.equals(testRegistrar.getRegistrarId()))
|
||||
.collect(toImmutableList()))
|
||||
.containsExactly(testRegistrarPoc1);
|
||||
.containsExactly(adminPoc);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -264,11 +278,7 @@ class ContactActionTest {
|
||||
Action.Method.POST,
|
||||
AuthResult.createUser(createAdminUser("email@email.com")),
|
||||
testRegistrar.getRegistrarId(),
|
||||
testRegistrarPoc1
|
||||
.asBuilder()
|
||||
.setPhoneNumber(null)
|
||||
.setVisibleInDomainWhoisAsAbuse(true)
|
||||
.build());
|
||||
adminPoc.asBuilder().setPhoneNumber(null).setVisibleInDomainWhoisAsAbuse(true).build());
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
@@ -282,14 +292,14 @@ class ContactActionTest {
|
||||
|
||||
@Test
|
||||
void testFailure_postUpdateContactInfo_whoisContactPhoneNumberRemoved() throws IOException {
|
||||
testRegistrarPoc1 = testRegistrarPoc1.asBuilder().setVisibleInDomainWhoisAsAbuse(true).build();
|
||||
insertInDb(testRegistrarPoc1);
|
||||
adminPoc = adminPoc.asBuilder().setVisibleInDomainWhoisAsAbuse(true).build();
|
||||
insertInDb(adminPoc);
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.createUser(createAdminUser("email@email.com")),
|
||||
testRegistrar.getRegistrarId(),
|
||||
testRegistrarPoc1.asBuilder().setVisibleInDomainWhoisAsAbuse(false).build());
|
||||
adminPoc.asBuilder().setVisibleInDomainWhoisAsAbuse(false).build());
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
@@ -298,7 +308,7 @@ class ContactActionTest {
|
||||
loadAllOf(RegistrarPoc.class).stream()
|
||||
.filter(r -> r.registrarId.equals(testRegistrar.getRegistrarId()))
|
||||
.collect(toImmutableList()))
|
||||
.containsExactly(testRegistrarPoc1);
|
||||
.containsExactly(adminPoc);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -309,7 +319,7 @@ class ContactActionTest {
|
||||
Action.Method.POST,
|
||||
AuthResult.createUser(createAdminUser("email@email.com")),
|
||||
testRegistrar.getRegistrarId(),
|
||||
testRegistrarPoc1
|
||||
adminPoc
|
||||
.asBuilder()
|
||||
.setAllowedToSetRegistryLockPassword(true)
|
||||
.setRegistryLockEmailAddress("lock@example.com")
|
||||
@@ -327,22 +337,19 @@ class ContactActionTest {
|
||||
|
||||
@Test
|
||||
void testFailure_postUpdateContactInfo_cannotModifyRegistryLockEmail() throws IOException {
|
||||
testRegistrarPoc1 =
|
||||
testRegistrarPoc1
|
||||
adminPoc =
|
||||
adminPoc
|
||||
.asBuilder()
|
||||
.setRegistryLockEmailAddress("lock@example.com")
|
||||
.setAllowedToSetRegistryLockPassword(true)
|
||||
.build();
|
||||
insertInDb(testRegistrarPoc1);
|
||||
insertInDb(adminPoc);
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.createUser(createAdminUser("email@email.com")),
|
||||
testRegistrar.getRegistrarId(),
|
||||
testRegistrarPoc1
|
||||
.asBuilder()
|
||||
.setRegistryLockEmailAddress("unlock@example.com")
|
||||
.build());
|
||||
adminPoc.asBuilder().setRegistryLockEmailAddress("unlock@example.com").build());
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
@@ -351,25 +358,25 @@ class ContactActionTest {
|
||||
loadAllOf(RegistrarPoc.class).stream()
|
||||
.filter(r -> r.registrarId.equals(testRegistrar.getRegistrarId()))
|
||||
.collect(toImmutableList()))
|
||||
.containsExactly(testRegistrarPoc1);
|
||||
.containsExactly(adminPoc);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_postUpdateContactInfo_cannotSetIsAllowedToSetRegistryLockPassword()
|
||||
throws IOException {
|
||||
testRegistrarPoc1 =
|
||||
testRegistrarPoc1
|
||||
adminPoc =
|
||||
adminPoc
|
||||
.asBuilder()
|
||||
.setRegistryLockEmailAddress("lock@example.com")
|
||||
.setAllowedToSetRegistryLockPassword(false)
|
||||
.build();
|
||||
insertInDb(testRegistrarPoc1);
|
||||
insertInDb(adminPoc);
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.createUser(createAdminUser("email@email.com")),
|
||||
testRegistrar.getRegistrarId(),
|
||||
testRegistrarPoc1.asBuilder().setAllowedToSetRegistryLockPassword(true).build());
|
||||
adminPoc.asBuilder().setAllowedToSetRegistryLockPassword(true).build());
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
@@ -378,18 +385,18 @@ class ContactActionTest {
|
||||
loadAllOf(RegistrarPoc.class).stream()
|
||||
.filter(r -> r.registrarId.equals(testRegistrar.getRegistrarId()))
|
||||
.collect(toImmutableList()))
|
||||
.containsExactly(testRegistrarPoc1);
|
||||
.containsExactly(adminPoc);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_sendsEmail() throws IOException, AddressException {
|
||||
insertInDb(testRegistrarPoc1.asBuilder().setEmailAddress("incorrect@email.com").build());
|
||||
insertInDb(techPoc.asBuilder().setEmailAddress("incorrect@email.com").build());
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.createUser(createAdminUser("email@email.com")),
|
||||
testRegistrar.getRegistrarId(),
|
||||
testRegistrarPoc1);
|
||||
techPoc);
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
|
||||
verify(consoleApiParams.sendEmailUtils().gmailClient, times(1))
|
||||
@@ -404,44 +411,42 @@ class ContactActionTest {
|
||||
+ "\n"
|
||||
+ "contacts:\n"
|
||||
+ " ADDED:\n"
|
||||
+ " {name=Test Registrar 1,"
|
||||
+ " emailAddress=test.registrar1@example.com, registrarId=registrarId,"
|
||||
+ " registryLockEmailAddress=null, phoneNumber=+1.9999999999,"
|
||||
+ " faxNumber=+1.9999999991, types=[ADMIN],"
|
||||
+ " visibleInWhoisAsAdmin=true, visibleInWhoisAsTech=false,"
|
||||
+ " {name=Test Registrar 2,"
|
||||
+ " emailAddress=test.registrar2@example.com, registrarId=registrarId,"
|
||||
+ " registryLockEmailAddress=null, phoneNumber=+1.1234567890,"
|
||||
+ " faxNumber=+1.1234567891, types=[TECH],"
|
||||
+ " visibleInWhoisAsAdmin=false, visibleInWhoisAsTech=true,"
|
||||
+ " visibleInDomainWhoisAsAbuse=false,"
|
||||
+ " allowedToSetRegistryLockPassword=false}\n"
|
||||
+ " REMOVED:\n"
|
||||
+ " {name=Test Registrar 1, emailAddress=incorrect@email.com,"
|
||||
+ " {name=Test Registrar 2, emailAddress=incorrect@email.com,"
|
||||
+ " registrarId=registrarId, registryLockEmailAddress=null,"
|
||||
+ " phoneNumber=+1.9999999999, faxNumber=+1.9999999991, types=[ADMIN],"
|
||||
+ " visibleInWhoisAsAdmin=true,"
|
||||
+ " visibleInWhoisAsTech=false, visibleInDomainWhoisAsAbuse=false,"
|
||||
+ " phoneNumber=+1.1234567890, faxNumber=+1.1234567891, types=[TECH],"
|
||||
+ " visibleInWhoisAsAdmin=false,"
|
||||
+ " visibleInWhoisAsTech=true, visibleInDomainWhoisAsAbuse=false,"
|
||||
+ " allowedToSetRegistryLockPassword=false}\n"
|
||||
+ " FINAL CONTENTS:\n"
|
||||
+ " {name=Test Registrar 1,"
|
||||
+ " emailAddress=test.registrar1@example.com, registrarId=registrarId,"
|
||||
+ " registryLockEmailAddress=null, phoneNumber=+1.9999999999,"
|
||||
+ " faxNumber=+1.9999999991, types=[ADMIN],"
|
||||
+ " visibleInWhoisAsAdmin=true, visibleInWhoisAsTech=false,"
|
||||
+ " {name=Test Registrar 2,"
|
||||
+ " emailAddress=test.registrar2@example.com, registrarId=registrarId,"
|
||||
+ " registryLockEmailAddress=null, phoneNumber=+1.1234567890,"
|
||||
+ " faxNumber=+1.1234567891, types=[TECH],"
|
||||
+ " visibleInWhoisAsAdmin=false, visibleInWhoisAsTech=true,"
|
||||
+ " visibleInDomainWhoisAsAbuse=false,"
|
||||
+ " allowedToSetRegistryLockPassword=false}\n")
|
||||
.setRecipients(
|
||||
ImmutableList.of(
|
||||
new InternetAddress("notification@test.example"),
|
||||
new InternetAddress("incorrect@email.com")))
|
||||
.setRecipients(ImmutableList.of(new InternetAddress("notification@test.example")))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_postDeleteContactInfo() throws IOException {
|
||||
insertInDb(testRegistrarPoc1);
|
||||
insertInDb(adminPoc, techPoc, marketingPoc);
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.createUser(createAdminUser("email@email.com")),
|
||||
testRegistrar.getRegistrarId(),
|
||||
testRegistrarPoc2);
|
||||
adminPoc,
|
||||
techPoc);
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(
|
||||
@@ -449,12 +454,12 @@ class ContactActionTest {
|
||||
.filter(r -> r.registrarId.equals(testRegistrar.getRegistrarId()))
|
||||
.map(r -> r.getName())
|
||||
.collect(toImmutableList()))
|
||||
.containsExactly("Test Registrar 2");
|
||||
.containsExactly("Test Registrar 1", "Test Registrar 2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_postDeleteContactInfo_missingPermission() throws IOException {
|
||||
insertInDb(testRegistrarPoc1);
|
||||
insertInDb(adminPoc);
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
@@ -469,11 +474,27 @@ class ContactActionTest {
|
||||
.build())
|
||||
.build()),
|
||||
testRegistrar.getRegistrarId(),
|
||||
testRegistrarPoc2);
|
||||
techPoc);
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_changesAdminEmail() throws Exception {
|
||||
insertInDb(adminPoc.asBuilder().setEmailAddress("oldemail@example.com").build());
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.createUser(createAdminUser("email@email.com")),
|
||||
testRegistrar.getRegistrarId(),
|
||||
adminPoc);
|
||||
action.run();
|
||||
FakeResponse fakeResponse = (FakeResponse) consoleApiParams.response();
|
||||
assertThat(fakeResponse.getStatus()).isEqualTo(400);
|
||||
assertThat(fakeResponse.getPayload())
|
||||
.isEqualTo("Cannot remove or change the email address of primary contacts");
|
||||
}
|
||||
|
||||
private ContactAction createAction(
|
||||
Action.Method method, AuthResult authResult, String registrarId, RegistrarPoc... contacts)
|
||||
throws IOException {
|
||||
|
||||
@@ -16,13 +16,18 @@ package google.registry.webdriver;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.server.Fixture.BASIC;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.server.RegistryTestServer;
|
||||
import java.util.List;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Timeout;
|
||||
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.junitpioneer.jupiter.RetryingTest;
|
||||
@@ -50,7 +55,15 @@ import org.openqa.selenium.WebElement;
|
||||
*/
|
||||
// The Selenium image only supports amd64 architecture.
|
||||
@EnabledIfSystemProperty(named = "os.arch", matches = "amd64")
|
||||
public class ConsoleScreenshotTest extends WebDriverTestCase {
|
||||
@Timeout(120)
|
||||
public class ConsoleScreenshotTest {
|
||||
|
||||
@RegisterExtension
|
||||
static final DockerWebDriverExtension webDriverProvider = new DockerWebDriverExtension();
|
||||
|
||||
@RegisterExtension
|
||||
final WebDriverPlusScreenDifferExtension driver =
|
||||
new WebDriverPlusScreenDifferExtension(webDriverProvider::getWebDriver);
|
||||
|
||||
@RegisterExtension
|
||||
final TestServerExtension server =
|
||||
@@ -65,6 +78,10 @@ public class ConsoleScreenshotTest extends WebDriverTestCase {
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
server.setRegistrarRoles(ImmutableMap.of("TheRegistrar", RegistrarRole.ACCOUNT_MANAGER));
|
||||
Registrar registrar = Registrar.loadByRegistrarId("TheRegistrar").get();
|
||||
registrar =
|
||||
registrar.asBuilder().setLastPocVerificationDate(DateTime.now(DateTimeZone.UTC)).build();
|
||||
persistResource(registrar);
|
||||
loadHomePage();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.webdriver;
|
||||
|
||||
import org.junit.jupiter.api.Timeout;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Base class for tests that needs a {@link WebDriverPlusScreenDifferExtension}. */
|
||||
@Timeout(120)
|
||||
class WebDriverTestCase {
|
||||
|
||||
@RegisterExtension
|
||||
static final DockerWebDriverExtension webDriverProvider = new DockerWebDriverExtension();
|
||||
|
||||
@RegisterExtension
|
||||
final WebDriverPlusScreenDifferExtension driver =
|
||||
new WebDriverPlusScreenDifferExtension(webDriverProvider::getWebDriver);
|
||||
}
|
||||
+13
@@ -6,6 +6,7 @@
|
||||
<domain:name>example1.tld</domain:name>
|
||||
<domain:name>example2.example</domain:name>
|
||||
<domain:name>reserved.tld</domain:name>
|
||||
<domain:name>rich.example</domain:name>
|
||||
</domain:check>
|
||||
</check>
|
||||
<extension>
|
||||
@@ -34,6 +35,12 @@
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
</fee:domain>
|
||||
<fee:domain>
|
||||
<fee:name>rich.example</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
</fee:domain>
|
||||
<fee:domain>
|
||||
<fee:name>example1.tld</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
@@ -52,6 +59,12 @@
|
||||
<fee:command>renew</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
</fee:domain>
|
||||
<fee:domain>
|
||||
<fee:name>rich.example</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>renew</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
</fee:domain>
|
||||
</fee:check>
|
||||
</extension>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
|
||||
+18
@@ -17,6 +17,10 @@
|
||||
<domain:name avail="false">reserved.tld</domain:name>
|
||||
<domain:reason>Alloc token invalid for domain</domain:reason>
|
||||
</domain:cd>
|
||||
<domain:cd>
|
||||
<domain:name avail="false">rich.example</domain:name>
|
||||
<domain:reason>Alloc token invalid for domain</domain:reason>
|
||||
</domain:cd>
|
||||
</domain:chkData>
|
||||
</resData>
|
||||
<extension>
|
||||
@@ -42,6 +46,13 @@
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:class>token-not-supported</fee:class>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
<fee:name>rich.example</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:class>token-not-supported</fee:class>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
<fee:name>example1.tld</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
@@ -64,6 +75,13 @@
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:class>token-not-supported</fee:class>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
<fee:name>rich.example</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>renew</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:class>token-not-supported</fee:class>
|
||||
</fee:cd>
|
||||
</fee:chkData>
|
||||
</extension>
|
||||
<trID>
|
||||
|
||||
+22
-3
@@ -16,6 +16,9 @@
|
||||
<domain:name avail="false">reserved.tld</domain:name>
|
||||
<domain:reason>Reserved</domain:reason>
|
||||
</domain:cd>
|
||||
<domain:cd>
|
||||
<domain:name avail="true">rich.example</domain:name>
|
||||
</domain:cd>
|
||||
</domain:chkData>
|
||||
</resData>
|
||||
<extension>
|
||||
@@ -41,26 +44,42 @@
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:class>reserved</fee:class>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
<fee:name>rich.example</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">100.00</fee:fee>
|
||||
<fee:class>premium</fee:class>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
<fee:name>example1.tld</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>renew</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:class>token-not-supported</fee:class>
|
||||
<fee:fee description="renew">11.00</fee:fee>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
<fee:name>example2.example</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>renew</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:class>token-not-supported</fee:class>
|
||||
<fee:fee description="renew">11.00</fee:fee>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
<fee:name>reserved.tld</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>renew</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:class>token-not-supported</fee:class>
|
||||
<fee:fee description="renew">11.00</fee:fee>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
<fee:name>rich.example</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>renew</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="renew">100.00</fee:fee>
|
||||
<fee:class>premium</fee:class>
|
||||
</fee:cd>
|
||||
</fee:chkData>
|
||||
</extension>
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
</domain:cd>
|
||||
<domain:cd>
|
||||
<domain:name avail="false">reserved.tld</domain:name>
|
||||
<domain:reason>Reserved</domain:reason>
|
||||
<domain:reason>Alloc token invalid for domain</domain:reason>
|
||||
</domain:cd>
|
||||
<domain:cd>
|
||||
<domain:name avail="true">specificuse.tld</domain:name>
|
||||
|
||||
+4
-4
@@ -17,14 +17,14 @@
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="create">11.70</fee:fee>
|
||||
</fee:cd>
|
||||
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||
<fee:name>example1.tld</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>renew</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:class>token-not-supported</fee:class>
|
||||
<fee:fee description="renew">11.00</fee:fee>
|
||||
</fee:cd>
|
||||
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||
<fee:name>example1.tld</fee:name>
|
||||
@@ -38,14 +38,14 @@
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>restore</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:class>token-not-supported</fee:class>
|
||||
<fee:fee description="restore">17.00</fee:fee>
|
||||
</fee:cd>
|
||||
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||
<fee:name>example1.tld</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>update</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:class>token-not-supported</fee:class>
|
||||
<fee:fee description="update">0.00</fee:fee>
|
||||
</fee:cd>
|
||||
</fee:chkData>
|
||||
</extension>
|
||||
|
||||
+4
-4
@@ -19,7 +19,7 @@
|
||||
</fee:object>
|
||||
<fee:command name="create">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="create">11.70</fee:fee>
|
||||
</fee:command>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
@@ -28,7 +28,7 @@
|
||||
</fee:object>
|
||||
<fee:command name="renew">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:class>token-not-supported</fee:class>
|
||||
<fee:fee description="renew">11.00</fee:fee>
|
||||
</fee:command>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
@@ -46,7 +46,7 @@
|
||||
</fee:object>
|
||||
<fee:command name="restore">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:class>token-not-supported</fee:class>
|
||||
<fee:fee description="restore">17.00</fee:fee>
|
||||
</fee:command>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
@@ -55,7 +55,7 @@
|
||||
</fee:object>
|
||||
<fee:command name="update">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:class>token-not-supported</fee:class>
|
||||
<fee:fee description="update">0.00</fee:fee>
|
||||
</fee:command>
|
||||
</fee:cd>
|
||||
</fee:chkData>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<check>
|
||||
<domain:check
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>%DOMAIN%</domain:name>
|
||||
</domain:check>
|
||||
</check>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:period unit="y">1</domain:period>
|
||||
<domain:registrant>crr-admin</domain:registrant>
|
||||
<domain:contact type="admin">crr-admin</domain:contact>
|
||||
<domain:contact type="tech">crr-tech</domain:contact>
|
||||
<domain:authInfo>
|
||||
<domain:pw>abcdefghijklmnop</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:create>
|
||||
</create>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
@@ -6,9 +6,6 @@
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:period unit="y">1</domain:period>
|
||||
<domain:registrant>crr-admin</domain:registrant>
|
||||
<domain:contact type="admin">crr-admin</domain:contact>
|
||||
<domain:contact type="tech">crr-tech</domain:contact>
|
||||
<domain:authInfo>
|
||||
<domain:pw>abcdefghijklmnop</domain:pw>
|
||||
</domain:authInfo>
|
||||
|
||||
@@ -261,26 +261,26 @@ td.section {
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">generated on</td>
|
||||
<td class="property_value">2025-04-18 20:02:20</td>
|
||||
<td class="property_value">2025-05-15 19:22:21</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">last flyway file</td>
|
||||
<td id="lastFlywayFile" class="property_value">V192__add_last_poc_verification_date.sql</td>
|
||||
<td id="lastFlywayFile" class="property_value">V194__password_reset_request_registrar.sql</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p> </p>
|
||||
<p> </p>
|
||||
<svg viewBox="0.00 0.00 4903.00 3666.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="erDiagram" style="overflow: hidden; width: 100%; height: 800px">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 3662)">
|
||||
<svg viewBox="0.00 0.00 4903.00 3732.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="erDiagram" style="overflow: hidden; width: 100%; height: 800px">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 3728)">
|
||||
<title>
|
||||
SchemaCrawler_Diagram
|
||||
</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-3662 4899,-3662 4899,4 -4,4" />
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-3728 4899,-3728 4899,4 -4,4" />
|
||||
<text text-anchor="start" x="4655" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated by</text>
|
||||
<text text-anchor="start" x="4738" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">SchemaCrawler 16.25.2</text>
|
||||
<text text-anchor="start" x="4654" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated on</text>
|
||||
<text text-anchor="start" x="4738" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">2025-04-18 20:02:20</text>
|
||||
<text text-anchor="start" x="4738" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">2025-05-15 19:22:21</text>
|
||||
<polygon fill="none" stroke="#888888" points="4651,-4 4651,-44 4887,-44 4887,-4 4651,-4" /> <!-- allocationtoken_a08ccbef -->
|
||||
<g id="node1" class="node">
|
||||
<title>
|
||||
@@ -404,7 +404,7 @@ td.section {
|
||||
<polyline fill="none" stroke="black" points="769.5,-845.99 774.5,-845.99 " />
|
||||
<text text-anchor="start" x="790" y="-849.8" font-family="Helvetica,sans-Serif" font-size="14.00">fk_billing_event_cancellation_matching_billing_recurrence_id</text>
|
||||
</g> <!-- registrar_6e1503e3 -->
|
||||
<g id="node35" class="node">
|
||||
<g id="node36" class="node">
|
||||
<title>
|
||||
registrar_6e1503e3
|
||||
</title>
|
||||
@@ -765,7 +765,7 @@ td.section {
|
||||
<polyline fill="none" stroke="black" points="209.49,-1035.98 213.47,-1032.96 " />
|
||||
<text text-anchor="start" x="1269" y="-620.8" font-family="Helvetica,sans-Serif" font-size="14.00">fk_domain_transfer_losing_registrar_id</text>
|
||||
</g> <!-- tld_f1fa57e2 -->
|
||||
<g id="node46" class="node">
|
||||
<g id="node47" class="node">
|
||||
<title>
|
||||
tld_f1fa57e2
|
||||
</title>
|
||||
@@ -1153,7 +1153,7 @@ td.section {
|
||||
<text text-anchor="start" x="4742.5" y="-1956.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4532,-1950.5 4532,-2047.5 4867,-2047.5 4867,-1950.5 4532,-1950.5" />
|
||||
</g> <!-- user_f2216f01 -->
|
||||
<g id="node48" class="node">
|
||||
<g id="node49" class="node">
|
||||
<title>
|
||||
user_f2216f01
|
||||
</title>
|
||||
@@ -1900,74 +1900,87 @@ td.section {
|
||||
<text text-anchor="start" x="4719.5" y="-2648.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4739.5" y="-2648.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4551,-2642 4551,-2720 4848,-2720 4848,-2642 4551,-2642" />
|
||||
</g> <!-- premiumentry_b0060b91 -->
|
||||
</g> <!-- passwordresetrequest_8484e7b1 -->
|
||||
<g id="node32" class="node">
|
||||
<title>
|
||||
passwordresetrequest_8484e7b1
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4554.5,-2766 4554.5,-2785 4770.5,-2785 4770.5,-2766 4554.5,-2766" />
|
||||
<text text-anchor="start" x="4556.5" y="-2772.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."PasswordResetRequest"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4770.5,-2766 4770.5,-2785 4844.5,-2785 4844.5,-2766 4770.5,-2766" />
|
||||
<text text-anchor="start" x="4805.5" y="-2771.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4556.5" y="-2753.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">verification_code</text>
|
||||
<text text-anchor="start" x="4718.5" y="-2752.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4772.5" y="-2752.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4553.5,-2746 4553.5,-2786 4845.5,-2786 4845.5,-2746 4553.5,-2746" />
|
||||
</g> <!-- premiumentry_b0060b91 -->
|
||||
<g id="node33" class="node">
|
||||
<title>
|
||||
premiumentry_b0060b91
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4585.5,-2785 4585.5,-2804 4740.5,-2804 4740.5,-2785 4585.5,-2785" />
|
||||
<text text-anchor="start" x="4587.5" y="-2791.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."PremiumEntry"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4740.5,-2785 4740.5,-2804 4814.5,-2804 4814.5,-2785 4740.5,-2785" />
|
||||
<text text-anchor="start" x="4775.5" y="-2790.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4587.5" y="-2772.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4706.5" y="-2771.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4742.5" y="-2771.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<text text-anchor="start" x="4587.5" y="-2753.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">domain_label</text>
|
||||
<text text-anchor="start" x="4706.5" y="-2752.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4742.5" y="-2752.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4584,-2746.5 4584,-2805.5 4815,-2805.5 4815,-2746.5 4584,-2746.5" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4585.5,-2851 4585.5,-2870 4740.5,-2870 4740.5,-2851 4585.5,-2851" />
|
||||
<text text-anchor="start" x="4587.5" y="-2857.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."PremiumEntry"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4740.5,-2851 4740.5,-2870 4814.5,-2870 4814.5,-2851 4740.5,-2851" />
|
||||
<text text-anchor="start" x="4775.5" y="-2856.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4587.5" y="-2838.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4706.5" y="-2837.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4742.5" y="-2837.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<text text-anchor="start" x="4587.5" y="-2819.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">domain_label</text>
|
||||
<text text-anchor="start" x="4706.5" y="-2818.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4742.5" y="-2818.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4584,-2812.5 4584,-2871.5 4815,-2871.5 4815,-2812.5 4584,-2812.5" />
|
||||
</g> <!-- premiumlist_7c3ea68b -->
|
||||
<g id="node33" class="node">
|
||||
<g id="node34" class="node">
|
||||
<title>
|
||||
premiumlist_7c3ea68b
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="3921,-2784 3921,-2803 4066,-2803 4066,-2784 3921,-2784" />
|
||||
<text text-anchor="start" x="3923" y="-2790.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."PremiumList"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4066,-2784 4066,-2803 4176,-2803 4176,-2784 4066,-2784" />
|
||||
<text text-anchor="start" x="4137" y="-2789.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="3923" y="-2771.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4029" y="-2770.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4068" y="-2770.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="4029" y="-2751.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4068" y="-2751.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="3923" y="-2732.8" font-family="Helvetica,sans-Serif" font-size="14.00">name</text>
|
||||
<text text-anchor="start" x="4029" y="-2732.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4068" y="-2732.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="3919.5,-2726 3919.5,-2804 4176.5,-2804 4176.5,-2726 3919.5,-2726" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="3921,-2850 3921,-2869 4066,-2869 4066,-2850 3921,-2850" />
|
||||
<text text-anchor="start" x="3923" y="-2856.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."PremiumList"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4066,-2850 4066,-2869 4176,-2869 4176,-2850 4066,-2850" />
|
||||
<text text-anchor="start" x="4137" y="-2855.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="3923" y="-2837.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4029" y="-2836.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4068" y="-2836.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="4029" y="-2817.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4068" y="-2817.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="3923" y="-2798.8" font-family="Helvetica,sans-Serif" font-size="14.00">name</text>
|
||||
<text text-anchor="start" x="4029" y="-2798.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4068" y="-2798.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="3919.5,-2792 3919.5,-2870 4176.5,-2870 4176.5,-2792 3919.5,-2792" />
|
||||
</g> <!-- premiumentry_b0060b91->premiumlist_7c3ea68b -->
|
||||
<g id="edge36" class="edge">
|
||||
<title>
|
||||
premiumentry_b0060b91:w->premiumlist_7c3ea68b:e
|
||||
</title>
|
||||
<path fill="none" stroke="black" d="M4566.39,-2775C4403.38,-2775 4353.85,-2775 4187.11,-2775" />
|
||||
<polygon fill="black" stroke="black" points="4574.5,-2775 4584.5,-2779.5 4579.5,-2775 4584.5,-2775 4584.5,-2775 4584.5,-2775 4579.5,-2775 4584.5,-2770.5 4574.5,-2775 4574.5,-2775" />
|
||||
<ellipse fill="none" stroke="black" cx="4570.5" cy="-2775" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="4178,-2780 4178,-2770 4180,-2770 4180,-2780 4178,-2780" />
|
||||
<polyline fill="none" stroke="black" points="4177,-2775 4182,-2775 " />
|
||||
<polygon fill="black" stroke="black" points="4183,-2780 4183,-2770 4185,-2770 4185,-2780 4183,-2780" />
|
||||
<polyline fill="none" stroke="black" points="4182,-2775 4187,-2775 " />
|
||||
<text text-anchor="start" x="4273.5" y="-2778.8" font-family="Helvetica,sans-Serif" font-size="14.00">fko0gw90lpo1tuee56l0nb6y6g5</text>
|
||||
<path fill="none" stroke="black" d="M4566.39,-2841C4403.38,-2841 4353.85,-2841 4187.11,-2841" />
|
||||
<polygon fill="black" stroke="black" points="4574.5,-2841 4584.5,-2845.5 4579.5,-2841 4584.5,-2841 4584.5,-2841 4584.5,-2841 4579.5,-2841 4584.5,-2836.5 4574.5,-2841 4574.5,-2841" />
|
||||
<ellipse fill="none" stroke="black" cx="4570.5" cy="-2841" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="4178,-2846 4178,-2836 4180,-2836 4180,-2846 4178,-2846" />
|
||||
<polyline fill="none" stroke="black" points="4177,-2841 4182,-2841 " />
|
||||
<polygon fill="black" stroke="black" points="4183,-2846 4183,-2836 4185,-2836 4185,-2846 4183,-2846" />
|
||||
<polyline fill="none" stroke="black" points="4182,-2841 4187,-2841 " />
|
||||
<text text-anchor="start" x="4273.5" y="-2844.8" font-family="Helvetica,sans-Serif" font-size="14.00">fko0gw90lpo1tuee56l0nb6y6g5</text>
|
||||
</g> <!-- rderevision_83396864 -->
|
||||
<g id="node34" class="node">
|
||||
<g id="node35" class="node">
|
||||
<title>
|
||||
rderevision_83396864
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4589.5,-2890 4589.5,-2909 4732.5,-2909 4732.5,-2890 4589.5,-2890" />
|
||||
<text text-anchor="start" x="4591.5" y="-2896.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."RdeRevision"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4732.5,-2890 4732.5,-2909 4810.5,-2909 4810.5,-2890 4732.5,-2890" />
|
||||
<text text-anchor="start" x="4771.5" y="-2895.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4591.5" y="-2877.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">tld</text>
|
||||
<text text-anchor="start" x="4681.5" y="-2876.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4734.5" y="-2876.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="4591.5" y="-2858.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">mode</text>
|
||||
<text text-anchor="start" x="4681.5" y="-2857.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4734.5" y="-2857.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="4591.5" y="-2839.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">"date"</text>
|
||||
<text text-anchor="start" x="4681.5" y="-2838.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4734.5" y="-2838.8" font-family="Helvetica,sans-Serif" font-size="14.00">date not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4588,-2832 4588,-2910 4811,-2910 4811,-2832 4588,-2832" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4589.5,-2956 4589.5,-2975 4732.5,-2975 4732.5,-2956 4589.5,-2956" />
|
||||
<text text-anchor="start" x="4591.5" y="-2962.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."RdeRevision"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4732.5,-2956 4732.5,-2975 4810.5,-2975 4810.5,-2956 4732.5,-2956" />
|
||||
<text text-anchor="start" x="4771.5" y="-2961.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4591.5" y="-2943.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">tld</text>
|
||||
<text text-anchor="start" x="4681.5" y="-2942.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4734.5" y="-2942.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="4591.5" y="-2924.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">mode</text>
|
||||
<text text-anchor="start" x="4681.5" y="-2923.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4734.5" y="-2923.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="4591.5" y="-2905.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">"date"</text>
|
||||
<text text-anchor="start" x="4681.5" y="-2904.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4734.5" y="-2904.8" font-family="Helvetica,sans-Serif" font-size="14.00">date not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4588,-2898 4588,-2976 4811,-2976 4811,-2898 4588,-2898" />
|
||||
</g> <!-- registrarpoc_ab47054d -->
|
||||
<g id="node36" class="node">
|
||||
<g id="node37" class="node">
|
||||
<title>
|
||||
registrarpoc_ab47054d
|
||||
</title>
|
||||
@@ -1996,7 +2009,7 @@ td.section {
|
||||
<polyline fill="none" stroke="black" points="208.93,-1035.36 212.36,-1031.72 " />
|
||||
<text text-anchor="start" x="246" y="-222.8" font-family="Helvetica,sans-Serif" font-size="14.00">fk_registrar_poc_registrar_id</text>
|
||||
</g> <!-- registrarupdatehistory_8a38bed4 -->
|
||||
<g id="node37" class="node">
|
||||
<g id="node38" class="node">
|
||||
<title>
|
||||
registrarupdatehistory_8a38bed4
|
||||
</title>
|
||||
@@ -2028,7 +2041,7 @@ td.section {
|
||||
<polyline fill="none" stroke="black" points="209,-1035.43 212.49,-1031.85 " />
|
||||
<text text-anchor="start" x="231" y="-142.8" font-family="Helvetica,sans-Serif" font-size="14.00">fkregistrarupdatehistoryregistrarid</text>
|
||||
</g> <!-- registrarpocupdatehistory_31e5d9aa -->
|
||||
<g id="node38" class="node">
|
||||
<g id="node39" class="node">
|
||||
<title>
|
||||
registrarpocupdatehistory_31e5d9aa
|
||||
</title>
|
||||
@@ -2076,205 +2089,205 @@ td.section {
|
||||
<polyline fill="none" stroke="black" points="727.49,-165.72 732.48,-165.44 " />
|
||||
<text text-anchor="start" x="851.5" y="-151.8" font-family="Helvetica,sans-Serif" font-size="14.00">fkregistrarpocupdatehistoryemailaddress</text>
|
||||
</g> <!-- registrylock_ac88663e -->
|
||||
<g id="node39" class="node">
|
||||
<g id="node40" class="node">
|
||||
<title>
|
||||
registrylock_ac88663e
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4571.5,-3051 4571.5,-3070 4718.5,-3070 4718.5,-3051 4571.5,-3051" />
|
||||
<text text-anchor="start" x="4573.5" y="-3057.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."RegistryLock"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4718.5,-3051 4718.5,-3070 4828.5,-3070 4828.5,-3051 4718.5,-3051" />
|
||||
<text text-anchor="start" x="4789.5" y="-3056.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4573.5" y="-3038.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4699.5" y="-3037.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4720.5" y="-3037.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="4699.5" y="-3018.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4720.5" y="-3018.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="4573.5" y="-2999.8" font-family="Helvetica,sans-Serif" font-size="14.00">registrar_id</text>
|
||||
<text text-anchor="start" x="4699.5" y="-2999.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4720.5" y="-2999.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="4573.5" y="-2980.8" font-family="Helvetica,sans-Serif" font-size="14.00">repo_id</text>
|
||||
<text text-anchor="start" x="4699.5" y="-2980.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4720.5" y="-2980.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="4573.5" y="-2961.8" font-family="Helvetica,sans-Serif" font-size="14.00">verification_code</text>
|
||||
<text text-anchor="start" x="4699.5" y="-2961.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4720.5" y="-2961.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="4573.5" y="-2942.8" font-family="Helvetica,sans-Serif" font-size="14.00">relock_revision_id</text>
|
||||
<text text-anchor="start" x="4699.5" y="-2942.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4720.5" y="-2942.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8</text>
|
||||
<polygon fill="none" stroke="#888888" points="4570,-2936.5 4570,-3071.5 4829,-3071.5 4829,-2936.5 4570,-2936.5" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4571.5,-3117 4571.5,-3136 4718.5,-3136 4718.5,-3117 4571.5,-3117" />
|
||||
<text text-anchor="start" x="4573.5" y="-3123.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."RegistryLock"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4718.5,-3117 4718.5,-3136 4828.5,-3136 4828.5,-3117 4718.5,-3117" />
|
||||
<text text-anchor="start" x="4789.5" y="-3122.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4573.5" y="-3104.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4699.5" y="-3103.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4720.5" y="-3103.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="4699.5" y="-3084.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4720.5" y="-3084.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="4573.5" y="-3065.8" font-family="Helvetica,sans-Serif" font-size="14.00">registrar_id</text>
|
||||
<text text-anchor="start" x="4699.5" y="-3065.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4720.5" y="-3065.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="4573.5" y="-3046.8" font-family="Helvetica,sans-Serif" font-size="14.00">repo_id</text>
|
||||
<text text-anchor="start" x="4699.5" y="-3046.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4720.5" y="-3046.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="4573.5" y="-3027.8" font-family="Helvetica,sans-Serif" font-size="14.00">verification_code</text>
|
||||
<text text-anchor="start" x="4699.5" y="-3027.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4720.5" y="-3027.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="4573.5" y="-3008.8" font-family="Helvetica,sans-Serif" font-size="14.00">relock_revision_id</text>
|
||||
<text text-anchor="start" x="4699.5" y="-3008.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4720.5" y="-3008.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8</text>
|
||||
<polygon fill="none" stroke="#888888" points="4570,-3002.5 4570,-3137.5 4829,-3137.5 4829,-3002.5 4570,-3002.5" />
|
||||
</g> <!-- registrylock_ac88663e->registrylock_ac88663e -->
|
||||
<g id="edge64" class="edge">
|
||||
<title>
|
||||
registrylock_ac88663e:w->registrylock_ac88663e:e
|
||||
</title>
|
||||
<path fill="none" stroke="black" d="M4554.85,-2953.77C4489.6,-2992.23 4503.78,-3093.5 4700,-3093.5 4902.83,-3093.5 4911.14,-3073.57 4838.29,-3045.63" />
|
||||
<polygon fill="black" stroke="black" points="4562.44,-2950.23 4573.4,-2950.08 4566.97,-2948.11 4571.5,-2946 4571.5,-2946 4571.5,-2946 4566.97,-2948.11 4569.6,-2941.92 4562.44,-2950.23 4562.44,-2950.23" />
|
||||
<ellipse fill="none" stroke="black" cx="4558.81" cy="-2951.92" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="4827.7,-3047.04 4831.18,-3037.66 4833.05,-3038.36 4829.57,-3047.73 4827.7,-3047.04" />
|
||||
<polyline fill="none" stroke="black" points="4828.5,-3042 4833.19,-3043.74 " />
|
||||
<polygon fill="black" stroke="black" points="4832.39,-3048.77 4835.86,-3039.4 4837.74,-3040.09 4834.26,-3049.47 4832.39,-3048.77" />
|
||||
<polyline fill="none" stroke="black" points="4833.19,-3043.74 4837.88,-3045.48 " />
|
||||
<text text-anchor="start" x="4618" y="-3097.3" font-family="Helvetica,sans-Serif" font-size="14.00">fk2lhcwpxlnqijr96irylrh1707</text>
|
||||
<path fill="none" stroke="black" d="M4554.85,-3019.77C4489.6,-3058.23 4503.78,-3159.5 4700,-3159.5 4902.83,-3159.5 4911.14,-3139.57 4838.29,-3111.63" />
|
||||
<polygon fill="black" stroke="black" points="4562.44,-3016.23 4573.4,-3016.08 4566.97,-3014.11 4571.5,-3012 4571.5,-3012 4571.5,-3012 4566.97,-3014.11 4569.6,-3007.92 4562.44,-3016.23 4562.44,-3016.23" />
|
||||
<ellipse fill="none" stroke="black" cx="4558.81" cy="-3017.92" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="4827.7,-3113.04 4831.18,-3103.66 4833.05,-3104.36 4829.57,-3113.73 4827.7,-3113.04" />
|
||||
<polyline fill="none" stroke="black" points="4828.5,-3108 4833.19,-3109.74 " />
|
||||
<polygon fill="black" stroke="black" points="4832.39,-3114.77 4835.86,-3105.4 4837.74,-3106.09 4834.26,-3115.47 4832.39,-3114.77" />
|
||||
<polyline fill="none" stroke="black" points="4833.19,-3109.74 4837.88,-3111.48 " />
|
||||
<text text-anchor="start" x="4618" y="-3163.3" font-family="Helvetica,sans-Serif" font-size="14.00">fk2lhcwpxlnqijr96irylrh1707</text>
|
||||
</g> <!-- reservedentry_1a7b8520 -->
|
||||
<g id="node40" class="node">
|
||||
<g id="node41" class="node">
|
||||
<title>
|
||||
reservedentry_1a7b8520
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4584.5,-3169 4584.5,-3188 4741.5,-3188 4741.5,-3169 4584.5,-3169" />
|
||||
<text text-anchor="start" x="4586.5" y="-3175.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."ReservedEntry"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4741.5,-3169 4741.5,-3188 4815.5,-3188 4815.5,-3169 4741.5,-3169" />
|
||||
<text text-anchor="start" x="4776.5" y="-3174.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4586.5" y="-3156.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4706.5" y="-3155.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4743.5" y="-3155.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<text text-anchor="start" x="4586.5" y="-3137.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">domain_label</text>
|
||||
<text text-anchor="start" x="4706.5" y="-3136.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4743.5" y="-3136.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4583,-3130.5 4583,-3189.5 4816,-3189.5 4816,-3130.5 4583,-3130.5" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4584.5,-3235 4584.5,-3254 4741.5,-3254 4741.5,-3235 4584.5,-3235" />
|
||||
<text text-anchor="start" x="4586.5" y="-3241.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."ReservedEntry"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4741.5,-3235 4741.5,-3254 4815.5,-3254 4815.5,-3235 4741.5,-3235" />
|
||||
<text text-anchor="start" x="4776.5" y="-3240.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4586.5" y="-3222.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4706.5" y="-3221.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4743.5" y="-3221.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<text text-anchor="start" x="4586.5" y="-3203.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">domain_label</text>
|
||||
<text text-anchor="start" x="4706.5" y="-3202.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4743.5" y="-3202.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4583,-3196.5 4583,-3255.5 4816,-3255.5 4816,-3196.5 4583,-3196.5" />
|
||||
</g> <!-- reservedlist_b97c3f1c -->
|
||||
<g id="node41" class="node">
|
||||
<g id="node42" class="node">
|
||||
<title>
|
||||
reservedlist_b97c3f1c
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="3920,-3168 3920,-3187 4066,-3187 4066,-3168 3920,-3168" />
|
||||
<text text-anchor="start" x="3922" y="-3174.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."ReservedList"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4066,-3168 4066,-3187 4176,-3187 4176,-3168 4066,-3168" />
|
||||
<text text-anchor="start" x="4137" y="-3173.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="3922" y="-3155.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4029" y="-3154.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4068" y="-3154.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="4029" y="-3135.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4068" y="-3135.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="3922" y="-3116.8" font-family="Helvetica,sans-Serif" font-size="14.00">name</text>
|
||||
<text text-anchor="start" x="4029" y="-3116.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4068" y="-3116.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="3919,-3110 3919,-3188 4177,-3188 4177,-3110 3919,-3110" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="3920,-3234 3920,-3253 4066,-3253 4066,-3234 3920,-3234" />
|
||||
<text text-anchor="start" x="3922" y="-3240.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."ReservedList"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4066,-3234 4066,-3253 4176,-3253 4176,-3234 4066,-3234" />
|
||||
<text text-anchor="start" x="4137" y="-3239.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="3922" y="-3221.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4029" y="-3220.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4068" y="-3220.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="4029" y="-3201.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4068" y="-3201.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="3922" y="-3182.8" font-family="Helvetica,sans-Serif" font-size="14.00">name</text>
|
||||
<text text-anchor="start" x="4029" y="-3182.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4068" y="-3182.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="3919,-3176 3919,-3254 4177,-3254 4177,-3176 3919,-3176" />
|
||||
</g> <!-- reservedentry_1a7b8520->reservedlist_b97c3f1c -->
|
||||
<g id="edge65" class="edge">
|
||||
<title>
|
||||
reservedentry_1a7b8520:w->reservedlist_b97c3f1c:e
|
||||
</title>
|
||||
<path fill="none" stroke="black" d="M4565.44,-3159C4402.83,-3159 4353.42,-3159 4187.08,-3159" />
|
||||
<polygon fill="black" stroke="black" points="4573.5,-3159 4583.5,-3163.5 4578.5,-3159 4583.5,-3159 4583.5,-3159 4583.5,-3159 4578.5,-3159 4583.5,-3154.5 4573.5,-3159 4573.5,-3159" />
|
||||
<ellipse fill="none" stroke="black" cx="4569.5" cy="-3159" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="4178,-3164 4178,-3154 4180,-3154 4180,-3164 4178,-3164" />
|
||||
<polyline fill="none" stroke="black" points="4177,-3159 4182,-3159 " />
|
||||
<polygon fill="black" stroke="black" points="4183,-3164 4183,-3154 4185,-3154 4185,-3164 4183,-3164" />
|
||||
<polyline fill="none" stroke="black" points="4182,-3159 4187,-3159 " />
|
||||
<text text-anchor="start" x="4275" y="-3162.8" font-family="Helvetica,sans-Serif" font-size="14.00">fkgq03rk0bt1hb915dnyvd3vnfc</text>
|
||||
<path fill="none" stroke="black" d="M4565.44,-3225C4402.83,-3225 4353.42,-3225 4187.08,-3225" />
|
||||
<polygon fill="black" stroke="black" points="4573.5,-3225 4583.5,-3229.5 4578.5,-3225 4583.5,-3225 4583.5,-3225 4583.5,-3225 4578.5,-3225 4583.5,-3220.5 4573.5,-3225 4573.5,-3225" />
|
||||
<ellipse fill="none" stroke="black" cx="4569.5" cy="-3225" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="4178,-3230 4178,-3220 4180,-3220 4180,-3230 4178,-3230" />
|
||||
<polyline fill="none" stroke="black" points="4177,-3225 4182,-3225 " />
|
||||
<polygon fill="black" stroke="black" points="4183,-3230 4183,-3220 4185,-3220 4185,-3230 4183,-3230" />
|
||||
<polyline fill="none" stroke="black" points="4182,-3225 4187,-3225 " />
|
||||
<text text-anchor="start" x="4275" y="-3228.8" font-family="Helvetica,sans-Serif" font-size="14.00">fkgq03rk0bt1hb915dnyvd3vnfc</text>
|
||||
</g> <!-- serversecret_6cc90f09 -->
|
||||
<g id="node42" class="node">
|
||||
<g id="node43" class="node">
|
||||
<title>
|
||||
serversecret_6cc90f09
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4590.5,-3236 4590.5,-3255 4735.5,-3255 4735.5,-3236 4590.5,-3236" />
|
||||
<text text-anchor="start" x="4592.5" y="-3242.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."ServerSecret"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4735.5,-3236 4735.5,-3255 4809.5,-3255 4809.5,-3236 4735.5,-3236" />
|
||||
<text text-anchor="start" x="4770.5" y="-3241.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4592.5" y="-3223.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">id</text>
|
||||
<text text-anchor="start" x="4669.5" y="-3222.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4737.5" y="-3222.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4589,-3216 4589,-3256 4810,-3256 4810,-3216 4589,-3216" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4590.5,-3302 4590.5,-3321 4735.5,-3321 4735.5,-3302 4590.5,-3302" />
|
||||
<text text-anchor="start" x="4592.5" y="-3308.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."ServerSecret"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4735.5,-3302 4735.5,-3321 4809.5,-3321 4809.5,-3302 4735.5,-3302" />
|
||||
<text text-anchor="start" x="4770.5" y="-3307.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4592.5" y="-3289.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">id</text>
|
||||
<text text-anchor="start" x="4669.5" y="-3288.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4737.5" y="-3288.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4589,-3282 4589,-3322 4810,-3322 4810,-3282 4589,-3282" />
|
||||
</g> <!-- signedmarkrevocationentry_99c39721 -->
|
||||
<g id="node43" class="node">
|
||||
<g id="node44" class="node">
|
||||
<title>
|
||||
signedmarkrevocationentry_99c39721
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4539.5,-3321 4539.5,-3340 4785.5,-3340 4785.5,-3321 4539.5,-3321" />
|
||||
<text text-anchor="start" x="4541.5" y="-3327.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."SignedMarkRevocationEntry"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4785.5,-3321 4785.5,-3340 4859.5,-3340 4859.5,-3321 4785.5,-3321" />
|
||||
<text text-anchor="start" x="4820.5" y="-3326.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4541.5" y="-3308.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4698.5" y="-3307.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4787.5" y="-3307.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<text text-anchor="start" x="4541.5" y="-3289.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">smd_id</text>
|
||||
<text text-anchor="start" x="4698.5" y="-3288.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4787.5" y="-3288.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4538.5,-3282.5 4538.5,-3341.5 4860.5,-3341.5 4860.5,-3282.5 4538.5,-3282.5" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4539.5,-3387 4539.5,-3406 4785.5,-3406 4785.5,-3387 4539.5,-3387" />
|
||||
<text text-anchor="start" x="4541.5" y="-3393.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."SignedMarkRevocationEntry"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4785.5,-3387 4785.5,-3406 4859.5,-3406 4859.5,-3387 4785.5,-3387" />
|
||||
<text text-anchor="start" x="4820.5" y="-3392.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4541.5" y="-3374.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4698.5" y="-3373.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4787.5" y="-3373.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<text text-anchor="start" x="4541.5" y="-3355.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">smd_id</text>
|
||||
<text text-anchor="start" x="4698.5" y="-3354.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4787.5" y="-3354.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4538.5,-3348.5 4538.5,-3407.5 4860.5,-3407.5 4860.5,-3348.5 4538.5,-3348.5" />
|
||||
</g> <!-- signedmarkrevocationlist_c5d968fb -->
|
||||
<g id="node44" class="node">
|
||||
<g id="node45" class="node">
|
||||
<title>
|
||||
signedmarkrevocationlist_c5d968fb
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="3875,-3321 3875,-3340 4111,-3340 4111,-3321 3875,-3321" />
|
||||
<text text-anchor="start" x="3877" y="-3327.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."SignedMarkRevocationList"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4111,-3321 4111,-3340 4221,-3340 4221,-3321 4111,-3321" />
|
||||
<text text-anchor="start" x="4182" y="-3326.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="3877" y="-3308.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4029" y="-3307.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4113" y="-3307.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="4029" y="-3288.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4113" y="-3288.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<polygon fill="none" stroke="#888888" points="3874,-3282.5 3874,-3341.5 4222,-3341.5 4222,-3282.5 3874,-3282.5" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="3875,-3387 3875,-3406 4111,-3406 4111,-3387 3875,-3387" />
|
||||
<text text-anchor="start" x="3877" y="-3393.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."SignedMarkRevocationList"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4111,-3387 4111,-3406 4221,-3406 4221,-3387 4111,-3387" />
|
||||
<text text-anchor="start" x="4182" y="-3392.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="3877" y="-3374.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4029" y="-3373.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4113" y="-3373.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="4029" y="-3354.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4113" y="-3354.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<polygon fill="none" stroke="#888888" points="3874,-3348.5 3874,-3407.5 4222,-3407.5 4222,-3348.5 3874,-3348.5" />
|
||||
</g> <!-- signedmarkrevocationentry_99c39721->signedmarkrevocationlist_c5d968fb -->
|
||||
<g id="edge66" class="edge">
|
||||
<title>
|
||||
signedmarkrevocationentry_99c39721:w->signedmarkrevocationlist_c5d968fb:e
|
||||
</title>
|
||||
<path fill="none" stroke="black" d="M4520.16,-3311C4397.65,-3311 4358.34,-3311 4232.05,-3311" />
|
||||
<polygon fill="black" stroke="black" points="4528.5,-3311 4538.5,-3315.5 4533.5,-3311 4538.5,-3311 4538.5,-3311 4538.5,-3311 4533.5,-3311 4538.5,-3306.5 4528.5,-3311 4528.5,-3311" />
|
||||
<ellipse fill="none" stroke="black" cx="4524.5" cy="-3311" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="4223,-3316 4223,-3306 4225,-3306 4225,-3316 4223,-3316" />
|
||||
<polyline fill="none" stroke="black" points="4222,-3311 4227,-3311 " />
|
||||
<polygon fill="black" stroke="black" points="4228,-3316 4228,-3306 4230,-3306 4230,-3316 4228,-3316" />
|
||||
<polyline fill="none" stroke="black" points="4227,-3311 4232,-3311 " />
|
||||
<text text-anchor="start" x="4284" y="-3314.8" font-family="Helvetica,sans-Serif" font-size="14.00">fk5ivlhvs3121yx2li5tqh54u4</text>
|
||||
<path fill="none" stroke="black" d="M4520.16,-3377C4397.65,-3377 4358.34,-3377 4232.05,-3377" />
|
||||
<polygon fill="black" stroke="black" points="4528.5,-3377 4538.5,-3381.5 4533.5,-3377 4538.5,-3377 4538.5,-3377 4538.5,-3377 4533.5,-3377 4538.5,-3372.5 4528.5,-3377 4528.5,-3377" />
|
||||
<ellipse fill="none" stroke="black" cx="4524.5" cy="-3377" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="4223,-3382 4223,-3372 4225,-3372 4225,-3382 4223,-3382" />
|
||||
<polyline fill="none" stroke="black" points="4222,-3377 4227,-3377 " />
|
||||
<polygon fill="black" stroke="black" points="4228,-3382 4228,-3372 4230,-3372 4230,-3382 4228,-3382" />
|
||||
<polyline fill="none" stroke="black" points="4227,-3377 4232,-3377 " />
|
||||
<text text-anchor="start" x="4284" y="-3380.8" font-family="Helvetica,sans-Serif" font-size="14.00">fk5ivlhvs3121yx2li5tqh54u4</text>
|
||||
</g> <!-- spec11threatmatch_a61228a6 -->
|
||||
<g id="node45" class="node">
|
||||
<g id="node46" class="node">
|
||||
<title>
|
||||
spec11threatmatch_a61228a6
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4549.5,-3464 4549.5,-3483 4739.5,-3483 4739.5,-3464 4549.5,-3464" />
|
||||
<text text-anchor="start" x="4551.5" y="-3470.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."Spec11ThreatMatch"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4739.5,-3464 4739.5,-3483 4849.5,-3483 4849.5,-3464 4739.5,-3464" />
|
||||
<text text-anchor="start" x="4810.5" y="-3469.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4551.5" y="-3451.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">id</text>
|
||||
<text text-anchor="start" x="4679.5" y="-3450.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4741.5" y="-3450.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="4679.5" y="-3431.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4741.5" y="-3431.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="4551.5" y="-3412.8" font-family="Helvetica,sans-Serif" font-size="14.00">check_date</text>
|
||||
<text text-anchor="start" x="4679.5" y="-3412.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4741.5" y="-3412.8" font-family="Helvetica,sans-Serif" font-size="14.00">date not null</text>
|
||||
<text text-anchor="start" x="4551.5" y="-3393.8" font-family="Helvetica,sans-Serif" font-size="14.00">registrar_id</text>
|
||||
<text text-anchor="start" x="4679.5" y="-3393.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4741.5" y="-3393.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="4551.5" y="-3374.8" font-family="Helvetica,sans-Serif" font-size="14.00">tld</text>
|
||||
<text text-anchor="start" x="4679.5" y="-3374.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4741.5" y="-3374.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4548.5,-3368 4548.5,-3484 4850.5,-3484 4850.5,-3368 4548.5,-3368" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4549.5,-3530 4549.5,-3549 4739.5,-3549 4739.5,-3530 4549.5,-3530" />
|
||||
<text text-anchor="start" x="4551.5" y="-3536.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."Spec11ThreatMatch"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4739.5,-3530 4739.5,-3549 4849.5,-3549 4849.5,-3530 4739.5,-3530" />
|
||||
<text text-anchor="start" x="4810.5" y="-3535.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4551.5" y="-3517.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">id</text>
|
||||
<text text-anchor="start" x="4679.5" y="-3516.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4741.5" y="-3516.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="4679.5" y="-3497.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4741.5" y="-3497.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="4551.5" y="-3478.8" font-family="Helvetica,sans-Serif" font-size="14.00">check_date</text>
|
||||
<text text-anchor="start" x="4679.5" y="-3478.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4741.5" y="-3478.8" font-family="Helvetica,sans-Serif" font-size="14.00">date not null</text>
|
||||
<text text-anchor="start" x="4551.5" y="-3459.8" font-family="Helvetica,sans-Serif" font-size="14.00">registrar_id</text>
|
||||
<text text-anchor="start" x="4679.5" y="-3459.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4741.5" y="-3459.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="4551.5" y="-3440.8" font-family="Helvetica,sans-Serif" font-size="14.00">tld</text>
|
||||
<text text-anchor="start" x="4679.5" y="-3440.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4741.5" y="-3440.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4548.5,-3434 4548.5,-3550 4850.5,-3550 4850.5,-3434 4548.5,-3434" />
|
||||
</g> <!-- tmchcrl_d282355 -->
|
||||
<g id="node47" class="node">
|
||||
<g id="node48" class="node">
|
||||
<title>
|
||||
tmchcrl_d282355
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4604.5,-3530 4604.5,-3549 4721.5,-3549 4721.5,-3530 4604.5,-3530" />
|
||||
<text text-anchor="start" x="4606.5" y="-3536.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."TmchCrl"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4721.5,-3530 4721.5,-3549 4795.5,-3549 4795.5,-3530 4721.5,-3530" />
|
||||
<text text-anchor="start" x="4756.5" y="-3535.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4606.5" y="-3517.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">id</text>
|
||||
<text text-anchor="start" x="4669.5" y="-3516.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4723.5" y="-3516.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4603,-3510 4603,-3550 4796,-3550 4796,-3510 4603,-3510" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4604.5,-3596 4604.5,-3615 4721.5,-3615 4721.5,-3596 4604.5,-3596" />
|
||||
<text text-anchor="start" x="4606.5" y="-3602.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."TmchCrl"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4721.5,-3596 4721.5,-3615 4795.5,-3615 4795.5,-3596 4721.5,-3596" />
|
||||
<text text-anchor="start" x="4756.5" y="-3601.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4606.5" y="-3583.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">id</text>
|
||||
<text text-anchor="start" x="4669.5" y="-3582.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4723.5" y="-3582.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4603,-3576 4603,-3616 4796,-3616 4796,-3576 4603,-3576" />
|
||||
</g> <!-- userupdatehistory_24efd476 -->
|
||||
<g id="node49" class="node">
|
||||
<g id="node50" class="node">
|
||||
<title>
|
||||
userupdatehistory_24efd476
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4570.5,-3634 4570.5,-3653 4754.5,-3653 4754.5,-3634 4570.5,-3634" />
|
||||
<text text-anchor="start" x="4572.5" y="-3640.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."UserUpdateHistory"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4754.5,-3634 4754.5,-3653 4828.5,-3653 4828.5,-3634 4754.5,-3634" />
|
||||
<text text-anchor="start" x="4789.5" y="-3639.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4572.5" y="-3621.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">history_revision_id</text>
|
||||
<text text-anchor="start" x="4724.5" y="-3620.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4756.5" y="-3620.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<text text-anchor="start" x="4572.5" y="-3601.8" font-family="Helvetica,sans-Serif" font-size="14.00">email_address</text>
|
||||
<text text-anchor="start" x="4724.5" y="-3601.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4756.5" y="-3601.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="4572.5" y="-3582.8" font-family="Helvetica,sans-Serif" font-size="14.00">history_acting_user</text>
|
||||
<text text-anchor="start" x="4724.5" y="-3582.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4756.5" y="-3582.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4569.5,-3576 4569.5,-3654 4829.5,-3654 4829.5,-3576 4569.5,-3576" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4570.5,-3700 4570.5,-3719 4754.5,-3719 4754.5,-3700 4570.5,-3700" />
|
||||
<text text-anchor="start" x="4572.5" y="-3706.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."UserUpdateHistory"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4754.5,-3700 4754.5,-3719 4828.5,-3719 4828.5,-3700 4754.5,-3700" />
|
||||
<text text-anchor="start" x="4789.5" y="-3705.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4572.5" y="-3687.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">history_revision_id</text>
|
||||
<text text-anchor="start" x="4724.5" y="-3686.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4756.5" y="-3686.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<text text-anchor="start" x="4572.5" y="-3667.8" font-family="Helvetica,sans-Serif" font-size="14.00">email_address</text>
|
||||
<text text-anchor="start" x="4724.5" y="-3667.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4756.5" y="-3667.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="4572.5" y="-3648.8" font-family="Helvetica,sans-Serif" font-size="14.00">history_acting_user</text>
|
||||
<text text-anchor="start" x="4724.5" y="-3648.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4756.5" y="-3648.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4569.5,-3642 4569.5,-3720 4829.5,-3720 4829.5,-3642 4569.5,-3642" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
@@ -4982,6 +4995,37 @@ td.section {
|
||||
</tbody>
|
||||
</table>
|
||||
<p> </p>
|
||||
<table>
|
||||
<caption style="background-color: #E9C2F2;">
|
||||
<span id="passwordresetrequest_8484e7b1" class="caption_name">public."PasswordResetRequest"</span> <span class="caption_description">[table]</span>
|
||||
</caption>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="spacer"></td>
|
||||
<td class="minwidth"><b><i>verification_code</i></b></td>
|
||||
<td class="minwidth">text not null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3" class="section">Primary Key</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="name">"PasswordResetRequest_pkey"</td>
|
||||
<td class="description right">[primary key]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"></td>
|
||||
<td class="minwidth">verification_code</td>
|
||||
<td class="minwidth"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p> </p>
|
||||
<table>
|
||||
<caption style="background-color: #E9C2F2;">
|
||||
<span id="pollmessage_614a523e" class="caption_name">public."PollMessage"</span> <span class="caption_description">[table]</span>
|
||||
|
||||
@@ -261,26 +261,26 @@ td.section {
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">generated on</td>
|
||||
<td class="property_value">2025-04-18 20:02:17</td>
|
||||
<td class="property_value">2025-05-15 19:22:16</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">last flyway file</td>
|
||||
<td id="lastFlywayFile" class="property_value">V192__add_last_poc_verification_date.sql</td>
|
||||
<td id="lastFlywayFile" class="property_value">V194__password_reset_request_registrar.sql</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p> </p>
|
||||
<p> </p>
|
||||
<svg viewBox="0.00 0.00 5683.00 7966.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="erDiagram" style="overflow: hidden; width: 100%; height: 800px">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 7962)">
|
||||
<svg viewBox="0.00 0.00 5683.00 8146.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="erDiagram" style="overflow: hidden; width: 100%; height: 800px">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 8142)">
|
||||
<title>
|
||||
SchemaCrawler_Diagram
|
||||
</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-7962 5679,-7962 5679,4 -4,4" />
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-8142 5679,-8142 5679,4 -4,4" />
|
||||
<text text-anchor="start" x="5435" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated by</text>
|
||||
<text text-anchor="start" x="5518" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">SchemaCrawler 16.25.2</text>
|
||||
<text text-anchor="start" x="5434" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated on</text>
|
||||
<text text-anchor="start" x="5518" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">2025-04-18 20:02:17</text>
|
||||
<text text-anchor="start" x="5518" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">2025-05-15 19:22:16</text>
|
||||
<polygon fill="none" stroke="#888888" points="5431,-4 5431,-44 5667,-44 5667,-4 5431,-4" /> <!-- allocationtoken_a08ccbef -->
|
||||
<g id="node1" class="node">
|
||||
<title>
|
||||
@@ -488,7 +488,7 @@ td.section {
|
||||
<polyline fill="none" stroke="black" points="1073.99,-2560.27 1078.99,-2560.54 " />
|
||||
<text text-anchor="start" x="1160" y="-2740.8" font-family="Helvetica,sans-Serif" font-size="14.00">fk_billing_event_cancellation_matching_billing_recurrence_id</text>
|
||||
</g> <!-- registrar_6e1503e3 -->
|
||||
<g id="node35" class="node">
|
||||
<g id="node36" class="node">
|
||||
<title>
|
||||
registrar_6e1503e3
|
||||
</title>
|
||||
@@ -1233,7 +1233,7 @@ td.section {
|
||||
<polyline fill="none" stroke="black" points="447.33,-3053.27 450.66,-3049.54 " />
|
||||
<text text-anchor="start" x="1639" y="-1837.8" font-family="Helvetica,sans-Serif" font-size="14.00">fk_domain_transfer_losing_registrar_id</text>
|
||||
</g> <!-- tld_f1fa57e2 -->
|
||||
<g id="node46" class="node">
|
||||
<g id="node47" class="node">
|
||||
<title>
|
||||
tld_f1fa57e2
|
||||
</title>
|
||||
@@ -1999,7 +1999,7 @@ td.section {
|
||||
<text text-anchor="start" x="5481" y="-5097.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5270.5,-5091 5270.5,-5245 5605.5,-5245 5605.5,-5091 5270.5,-5091" />
|
||||
</g> <!-- user_f2216f01 -->
|
||||
<g id="node48" class="node">
|
||||
<g id="node49" class="node">
|
||||
<title>
|
||||
user_f2216f01
|
||||
</title>
|
||||
@@ -3163,92 +3163,123 @@ td.section {
|
||||
<text text-anchor="start" x="5443" y="-6189.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5463" y="-6189.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5274.5,-6183 5274.5,-6375 5601.5,-6375 5601.5,-6183 5274.5,-6183" />
|
||||
</g> <!-- premiumentry_b0060b91 -->
|
||||
</g> <!-- passwordresetrequest_8484e7b1 -->
|
||||
<g id="node32" class="node">
|
||||
<title>
|
||||
passwordresetrequest_8484e7b1
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5267,-6535 5267,-6554 5483,-6554 5483,-6535 5267,-6535" />
|
||||
<text text-anchor="start" x="5269" y="-6541.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."PasswordResetRequest"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5483,-6535 5483,-6554 5609,-6554 5609,-6535 5483,-6535" />
|
||||
<text text-anchor="start" x="5570" y="-6540.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5269" y="-6521.8" font-family="Helvetica,sans-Serif" font-size="14.00">type</text>
|
||||
<text text-anchor="start" x="5431" y="-6521.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5485" y="-6521.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5269" y="-6502.8" font-family="Helvetica,sans-Serif" font-size="14.00">request_time</text>
|
||||
<text text-anchor="start" x="5431" y="-6502.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5485" y="-6502.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz not null</text>
|
||||
<text text-anchor="start" x="5269" y="-6483.8" font-family="Helvetica,sans-Serif" font-size="14.00">requester</text>
|
||||
<text text-anchor="start" x="5431" y="-6483.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5485" y="-6483.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5269" y="-6464.8" font-family="Helvetica,sans-Serif" font-size="14.00">fulfillment_time</text>
|
||||
<text text-anchor="start" x="5431" y="-6464.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5485" y="-6464.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz</text>
|
||||
<text text-anchor="start" x="5269" y="-6445.8" font-family="Helvetica,sans-Serif" font-size="14.00">destination_email</text>
|
||||
<text text-anchor="start" x="5431" y="-6445.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5485" y="-6445.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5269" y="-6427.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">verification_code</text>
|
||||
<text text-anchor="start" x="5431" y="-6426.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5485" y="-6426.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5269" y="-6407.8" font-family="Helvetica,sans-Serif" font-size="14.00">registrar_id</text>
|
||||
<text text-anchor="start" x="5431" y="-6407.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5485" y="-6407.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5266,-6401 5266,-6555 5610,-6555 5610,-6401 5266,-6401" />
|
||||
</g> <!-- premiumentry_b0060b91 -->
|
||||
<g id="node33" class="node">
|
||||
<title>
|
||||
premiumentry_b0060b91
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5291,-6459 5291,-6478 5446,-6478 5446,-6459 5291,-6459" />
|
||||
<text text-anchor="start" x="5293" y="-6465.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."PremiumEntry"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5446,-6459 5446,-6478 5586,-6478 5586,-6459 5446,-6459" />
|
||||
<text text-anchor="start" x="5547" y="-6464.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5293" y="-6446.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="5412" y="-6445.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5448" y="-6445.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<text text-anchor="start" x="5293" y="-6426.8" font-family="Helvetica,sans-Serif" font-size="14.00">price</text>
|
||||
<text text-anchor="start" x="5412" y="-6426.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5448" y="-6426.8" font-family="Helvetica,sans-Serif" font-size="14.00">numeric(19, 2) not null</text>
|
||||
<text text-anchor="start" x="5293" y="-6408.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">domain_label</text>
|
||||
<text text-anchor="start" x="5412" y="-6407.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5448" y="-6407.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5289.5,-6401 5289.5,-6479 5586.5,-6479 5586.5,-6401 5289.5,-6401" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5291,-6639 5291,-6658 5446,-6658 5446,-6639 5291,-6639" />
|
||||
<text text-anchor="start" x="5293" y="-6645.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."PremiumEntry"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5446,-6639 5446,-6658 5586,-6658 5586,-6639 5446,-6639" />
|
||||
<text text-anchor="start" x="5547" y="-6644.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5293" y="-6626.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="5412" y="-6625.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5448" y="-6625.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<text text-anchor="start" x="5293" y="-6606.8" font-family="Helvetica,sans-Serif" font-size="14.00">price</text>
|
||||
<text text-anchor="start" x="5412" y="-6606.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5448" y="-6606.8" font-family="Helvetica,sans-Serif" font-size="14.00">numeric(19, 2) not null</text>
|
||||
<text text-anchor="start" x="5293" y="-6588.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">domain_label</text>
|
||||
<text text-anchor="start" x="5412" y="-6587.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5448" y="-6587.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5289.5,-6581 5289.5,-6659 5586.5,-6659 5586.5,-6581 5289.5,-6581" />
|
||||
</g> <!-- premiumlist_7c3ea68b -->
|
||||
<g id="node33" class="node">
|
||||
<g id="node34" class="node">
|
||||
<title>
|
||||
premiumlist_7c3ea68b
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4618,-6459 4618,-6478 4763,-6478 4763,-6459 4618,-6459" />
|
||||
<text text-anchor="start" x="4620" y="-6465.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."PremiumList"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4763,-6459 4763,-6478 4873,-6478 4873,-6459 4763,-6459" />
|
||||
<text text-anchor="start" x="4834" y="-6464.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4620" y="-6446.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4750" y="-6445.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4765" y="-6445.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="4750" y="-6426.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4765" y="-6426.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="4620" y="-6407.8" font-family="Helvetica,sans-Serif" font-size="14.00">creation_timestamp</text>
|
||||
<text text-anchor="start" x="4750" y="-6407.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4765" y="-6407.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz</text>
|
||||
<text text-anchor="start" x="4620" y="-6388.8" font-family="Helvetica,sans-Serif" font-size="14.00">name</text>
|
||||
<text text-anchor="start" x="4750" y="-6388.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4765" y="-6388.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="4620" y="-6369.8" font-family="Helvetica,sans-Serif" font-size="14.00">bloom_filter</text>
|
||||
<text text-anchor="start" x="4750" y="-6369.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4765" y="-6369.8" font-family="Helvetica,sans-Serif" font-size="14.00">bytea not null</text>
|
||||
<text text-anchor="start" x="4620" y="-6350.8" font-family="Helvetica,sans-Serif" font-size="14.00">currency</text>
|
||||
<text text-anchor="start" x="4750" y="-6350.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4765" y="-6350.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4616.5,-6344.5 4616.5,-6479.5 4873.5,-6479.5 4873.5,-6344.5 4616.5,-6344.5" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4618,-6639 4618,-6658 4763,-6658 4763,-6639 4618,-6639" />
|
||||
<text text-anchor="start" x="4620" y="-6645.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."PremiumList"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4763,-6639 4763,-6658 4873,-6658 4873,-6639 4763,-6639" />
|
||||
<text text-anchor="start" x="4834" y="-6644.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4620" y="-6626.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4750" y="-6625.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4765" y="-6625.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="4750" y="-6606.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4765" y="-6606.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="4620" y="-6587.8" font-family="Helvetica,sans-Serif" font-size="14.00">creation_timestamp</text>
|
||||
<text text-anchor="start" x="4750" y="-6587.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4765" y="-6587.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz</text>
|
||||
<text text-anchor="start" x="4620" y="-6568.8" font-family="Helvetica,sans-Serif" font-size="14.00">name</text>
|
||||
<text text-anchor="start" x="4750" y="-6568.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4765" y="-6568.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="4620" y="-6549.8" font-family="Helvetica,sans-Serif" font-size="14.00">bloom_filter</text>
|
||||
<text text-anchor="start" x="4750" y="-6549.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4765" y="-6549.8" font-family="Helvetica,sans-Serif" font-size="14.00">bytea not null</text>
|
||||
<text text-anchor="start" x="4620" y="-6530.8" font-family="Helvetica,sans-Serif" font-size="14.00">currency</text>
|
||||
<text text-anchor="start" x="4750" y="-6530.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4765" y="-6530.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4616.5,-6524.5 4616.5,-6659.5 4873.5,-6659.5 4873.5,-6524.5 4616.5,-6524.5" />
|
||||
</g> <!-- premiumentry_b0060b91->premiumlist_7c3ea68b -->
|
||||
<g id="edge36" class="edge">
|
||||
<title>
|
||||
premiumentry_b0060b91:w->premiumlist_7c3ea68b:e
|
||||
</title>
|
||||
<path fill="none" stroke="black" d="M5271.77,-6450C5105.08,-6450 5054.58,-6450 4884.07,-6450" />
|
||||
<polygon fill="black" stroke="black" points="5280,-6450 5290,-6454.5 5285,-6450 5290,-6450 5290,-6450 5290,-6450 5285,-6450 5290,-6445.5 5280,-6450 5280,-6450" />
|
||||
<ellipse fill="none" stroke="black" cx="5276" cy="-6450" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="4875,-6455 4875,-6445 4877,-6445 4877,-6455 4875,-6455" />
|
||||
<polyline fill="none" stroke="black" points="4874,-6450 4879,-6450 " />
|
||||
<polygon fill="black" stroke="black" points="4880,-6455 4880,-6445 4882,-6445 4882,-6455 4880,-6455" />
|
||||
<polyline fill="none" stroke="black" points="4879,-6450 4884,-6450 " />
|
||||
<text text-anchor="start" x="4970.5" y="-6453.8" font-family="Helvetica,sans-Serif" font-size="14.00">fko0gw90lpo1tuee56l0nb6y6g5</text>
|
||||
<path fill="none" stroke="black" d="M5271.77,-6630C5105.08,-6630 5054.58,-6630 4884.07,-6630" />
|
||||
<polygon fill="black" stroke="black" points="5280,-6630 5290,-6634.5 5285,-6630 5290,-6630 5290,-6630 5290,-6630 5285,-6630 5290,-6625.5 5280,-6630 5280,-6630" />
|
||||
<ellipse fill="none" stroke="black" cx="5276" cy="-6630" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="4875,-6635 4875,-6625 4877,-6625 4877,-6635 4875,-6635" />
|
||||
<polyline fill="none" stroke="black" points="4874,-6630 4879,-6630 " />
|
||||
<polygon fill="black" stroke="black" points="4880,-6635 4880,-6625 4882,-6625 4882,-6635 4880,-6635" />
|
||||
<polyline fill="none" stroke="black" points="4879,-6630 4884,-6630 " />
|
||||
<text text-anchor="start" x="4970.5" y="-6633.8" font-family="Helvetica,sans-Serif" font-size="14.00">fko0gw90lpo1tuee56l0nb6y6g5</text>
|
||||
</g> <!-- rderevision_83396864 -->
|
||||
<g id="node34" class="node">
|
||||
<g id="node35" class="node">
|
||||
<title>
|
||||
rderevision_83396864
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5327,-6601 5327,-6620 5470,-6620 5470,-6601 5327,-6601" />
|
||||
<text text-anchor="start" x="5329" y="-6607.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."RdeRevision"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5470,-6601 5470,-6620 5549,-6620 5549,-6601 5470,-6601" />
|
||||
<text text-anchor="start" x="5510" y="-6606.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5329" y="-6588.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">tld</text>
|
||||
<text text-anchor="start" x="5455" y="-6587.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-6587.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5329" y="-6569.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">mode</text>
|
||||
<text text-anchor="start" x="5455" y="-6568.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-6568.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5329" y="-6550.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">"date"</text>
|
||||
<text text-anchor="start" x="5455" y="-6549.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-6549.8" font-family="Helvetica,sans-Serif" font-size="14.00">date not null</text>
|
||||
<text text-anchor="start" x="5329" y="-6530.8" font-family="Helvetica,sans-Serif" font-size="14.00">update_timestamp</text>
|
||||
<text text-anchor="start" x="5455" y="-6530.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-6530.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz</text>
|
||||
<text text-anchor="start" x="5329" y="-6511.8" font-family="Helvetica,sans-Serif" font-size="14.00">revision</text>
|
||||
<text text-anchor="start" x="5455" y="-6511.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-6511.8" font-family="Helvetica,sans-Serif" font-size="14.00">int4 not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5326,-6505 5326,-6621 5550,-6621 5550,-6505 5326,-6505" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5327,-6781 5327,-6800 5470,-6800 5470,-6781 5327,-6781" />
|
||||
<text text-anchor="start" x="5329" y="-6787.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."RdeRevision"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5470,-6781 5470,-6800 5549,-6800 5549,-6781 5470,-6781" />
|
||||
<text text-anchor="start" x="5510" y="-6786.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5329" y="-6768.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">tld</text>
|
||||
<text text-anchor="start" x="5455" y="-6767.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-6767.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5329" y="-6749.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">mode</text>
|
||||
<text text-anchor="start" x="5455" y="-6748.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-6748.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5329" y="-6730.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">"date"</text>
|
||||
<text text-anchor="start" x="5455" y="-6729.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-6729.8" font-family="Helvetica,sans-Serif" font-size="14.00">date not null</text>
|
||||
<text text-anchor="start" x="5329" y="-6710.8" font-family="Helvetica,sans-Serif" font-size="14.00">update_timestamp</text>
|
||||
<text text-anchor="start" x="5455" y="-6710.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-6710.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz</text>
|
||||
<text text-anchor="start" x="5329" y="-6691.8" font-family="Helvetica,sans-Serif" font-size="14.00">revision</text>
|
||||
<text text-anchor="start" x="5455" y="-6691.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-6691.8" font-family="Helvetica,sans-Serif" font-size="14.00">int4 not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5326,-6685 5326,-6801 5550,-6801 5550,-6685 5326,-6685" />
|
||||
</g> <!-- registrarpoc_ab47054d -->
|
||||
<g id="node36" class="node">
|
||||
<g id="node37" class="node">
|
||||
<title>
|
||||
registrarpoc_ab47054d
|
||||
</title>
|
||||
@@ -3310,7 +3341,7 @@ td.section {
|
||||
<polyline fill="none" stroke="black" points="446.71,-3052.8 449.42,-3048.6 " />
|
||||
<text text-anchor="start" x="485" y="-1380.8" font-family="Helvetica,sans-Serif" font-size="14.00">fk_registrar_poc_registrar_id</text>
|
||||
</g> <!-- registrarupdatehistory_8a38bed4 -->
|
||||
<g id="node37" class="node">
|
||||
<g id="node38" class="node">
|
||||
<title>
|
||||
registrarupdatehistory_8a38bed4
|
||||
</title>
|
||||
@@ -3492,7 +3523,7 @@ td.section {
|
||||
<polyline fill="none" stroke="black" points="445.56,-3052.25 447.12,-3047.5 " />
|
||||
<text text-anchor="start" x="470" y="-1110.8" font-family="Helvetica,sans-Serif" font-size="14.00">fkregistrarupdatehistoryregistrarid</text>
|
||||
</g> <!-- registrarpocupdatehistory_31e5d9aa -->
|
||||
<g id="node38" class="node">
|
||||
<g id="node39" class="node">
|
||||
<title>
|
||||
registrarpocupdatehistory_31e5d9aa
|
||||
</title>
|
||||
@@ -3591,304 +3622,304 @@ td.section {
|
||||
<polyline fill="none" stroke="black" points="1086,-1116.03 1091,-1116.05 " />
|
||||
<text text-anchor="start" x="1221.5" y="-1192.8" font-family="Helvetica,sans-Serif" font-size="14.00">fkregistrarpocupdatehistoryemailaddress</text>
|
||||
</g> <!-- registrylock_ac88663e -->
|
||||
<g id="node39" class="node">
|
||||
<g id="node40" class="node">
|
||||
<title>
|
||||
registrylock_ac88663e
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5296,-6933 5296,-6952 5455,-6952 5455,-6933 5296,-6933" />
|
||||
<text text-anchor="start" x="5298" y="-6939.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."RegistryLock"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5455,-6933 5455,-6952 5581,-6952 5581,-6933 5455,-6933" />
|
||||
<text text-anchor="start" x="5542" y="-6938.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5298" y="-6920.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="5449" y="-6919.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6919.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="5449" y="-6900.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6900.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="5298" y="-6881.8" font-family="Helvetica,sans-Serif" font-size="14.00">lock_completion_time</text>
|
||||
<text text-anchor="start" x="5449" y="-6881.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6881.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz</text>
|
||||
<text text-anchor="start" x="5298" y="-6862.8" font-family="Helvetica,sans-Serif" font-size="14.00">lock_request_time</text>
|
||||
<text text-anchor="start" x="5449" y="-6862.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6862.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz not null</text>
|
||||
<text text-anchor="start" x="5298" y="-6843.8" font-family="Helvetica,sans-Serif" font-size="14.00">domain_name</text>
|
||||
<text text-anchor="start" x="5449" y="-6843.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6843.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5298" y="-6824.8" font-family="Helvetica,sans-Serif" font-size="14.00">is_superuser</text>
|
||||
<text text-anchor="start" x="5449" y="-6824.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6824.8" font-family="Helvetica,sans-Serif" font-size="14.00">bool not null</text>
|
||||
<text text-anchor="start" x="5298" y="-6805.8" font-family="Helvetica,sans-Serif" font-size="14.00">registrar_id</text>
|
||||
<text text-anchor="start" x="5449" y="-6805.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6805.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5298" y="-6786.8" font-family="Helvetica,sans-Serif" font-size="14.00">registrar_poc_id</text>
|
||||
<text text-anchor="start" x="5449" y="-6786.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6786.8" font-family="Helvetica,sans-Serif" font-size="14.00">text</text>
|
||||
<text text-anchor="start" x="5298" y="-6767.8" font-family="Helvetica,sans-Serif" font-size="14.00">repo_id</text>
|
||||
<text text-anchor="start" x="5449" y="-6767.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6767.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5298" y="-6748.8" font-family="Helvetica,sans-Serif" font-size="14.00">verification_code</text>
|
||||
<text text-anchor="start" x="5449" y="-6748.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6748.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5298" y="-6729.8" font-family="Helvetica,sans-Serif" font-size="14.00">unlock_request_time</text>
|
||||
<text text-anchor="start" x="5449" y="-6729.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6729.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz</text>
|
||||
<text text-anchor="start" x="5298" y="-6710.8" font-family="Helvetica,sans-Serif" font-size="14.00">unlock_completion_time</text>
|
||||
<text text-anchor="start" x="5449" y="-6710.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6710.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz</text>
|
||||
<text text-anchor="start" x="5298" y="-6691.8" font-family="Helvetica,sans-Serif" font-size="14.00">last_update_time</text>
|
||||
<text text-anchor="start" x="5449" y="-6691.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6691.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz not null</text>
|
||||
<text text-anchor="start" x="5298" y="-6672.8" font-family="Helvetica,sans-Serif" font-size="14.00">relock_revision_id</text>
|
||||
<text text-anchor="start" x="5449" y="-6672.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6672.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8</text>
|
||||
<text text-anchor="start" x="5298" y="-6653.8" font-family="Helvetica,sans-Serif" font-size="14.00">relock_duration</text>
|
||||
<text text-anchor="start" x="5449" y="-6653.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6653.8" font-family="Helvetica,sans-Serif" font-size="14.00">interval</text>
|
||||
<polygon fill="none" stroke="#888888" points="5294.5,-6647 5294.5,-6953 5581.5,-6953 5581.5,-6647 5294.5,-6647" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5296,-7113 5296,-7132 5455,-7132 5455,-7113 5296,-7113" />
|
||||
<text text-anchor="start" x="5298" y="-7119.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."RegistryLock"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5455,-7113 5455,-7132 5581,-7132 5581,-7113 5455,-7113" />
|
||||
<text text-anchor="start" x="5542" y="-7118.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5298" y="-7100.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="5449" y="-7099.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-7099.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="5449" y="-7080.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-7080.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="5298" y="-7061.8" font-family="Helvetica,sans-Serif" font-size="14.00">lock_completion_time</text>
|
||||
<text text-anchor="start" x="5449" y="-7061.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-7061.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz</text>
|
||||
<text text-anchor="start" x="5298" y="-7042.8" font-family="Helvetica,sans-Serif" font-size="14.00">lock_request_time</text>
|
||||
<text text-anchor="start" x="5449" y="-7042.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-7042.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz not null</text>
|
||||
<text text-anchor="start" x="5298" y="-7023.8" font-family="Helvetica,sans-Serif" font-size="14.00">domain_name</text>
|
||||
<text text-anchor="start" x="5449" y="-7023.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-7023.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5298" y="-7004.8" font-family="Helvetica,sans-Serif" font-size="14.00">is_superuser</text>
|
||||
<text text-anchor="start" x="5449" y="-7004.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-7004.8" font-family="Helvetica,sans-Serif" font-size="14.00">bool not null</text>
|
||||
<text text-anchor="start" x="5298" y="-6985.8" font-family="Helvetica,sans-Serif" font-size="14.00">registrar_id</text>
|
||||
<text text-anchor="start" x="5449" y="-6985.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6985.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5298" y="-6966.8" font-family="Helvetica,sans-Serif" font-size="14.00">registrar_poc_id</text>
|
||||
<text text-anchor="start" x="5449" y="-6966.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6966.8" font-family="Helvetica,sans-Serif" font-size="14.00">text</text>
|
||||
<text text-anchor="start" x="5298" y="-6947.8" font-family="Helvetica,sans-Serif" font-size="14.00">repo_id</text>
|
||||
<text text-anchor="start" x="5449" y="-6947.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6947.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5298" y="-6928.8" font-family="Helvetica,sans-Serif" font-size="14.00">verification_code</text>
|
||||
<text text-anchor="start" x="5449" y="-6928.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6928.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5298" y="-6909.8" font-family="Helvetica,sans-Serif" font-size="14.00">unlock_request_time</text>
|
||||
<text text-anchor="start" x="5449" y="-6909.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6909.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz</text>
|
||||
<text text-anchor="start" x="5298" y="-6890.8" font-family="Helvetica,sans-Serif" font-size="14.00">unlock_completion_time</text>
|
||||
<text text-anchor="start" x="5449" y="-6890.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6890.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz</text>
|
||||
<text text-anchor="start" x="5298" y="-6871.8" font-family="Helvetica,sans-Serif" font-size="14.00">last_update_time</text>
|
||||
<text text-anchor="start" x="5449" y="-6871.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6871.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz not null</text>
|
||||
<text text-anchor="start" x="5298" y="-6852.8" font-family="Helvetica,sans-Serif" font-size="14.00">relock_revision_id</text>
|
||||
<text text-anchor="start" x="5449" y="-6852.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6852.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8</text>
|
||||
<text text-anchor="start" x="5298" y="-6833.8" font-family="Helvetica,sans-Serif" font-size="14.00">relock_duration</text>
|
||||
<text text-anchor="start" x="5449" y="-6833.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5457" y="-6833.8" font-family="Helvetica,sans-Serif" font-size="14.00">interval</text>
|
||||
<polygon fill="none" stroke="#888888" points="5294.5,-6827 5294.5,-7133 5581.5,-7133 5581.5,-6827 5294.5,-6827" />
|
||||
</g> <!-- registrylock_ac88663e->registrylock_ac88663e -->
|
||||
<g id="edge64" class="edge">
|
||||
<title>
|
||||
registrylock_ac88663e:w->registrylock_ac88663e:e
|
||||
</title>
|
||||
<path fill="none" stroke="black" d="M5281.52,-6687.14C5203.21,-6759.94 5215.41,-6975 5438.5,-6975 5666.43,-6975 5674.22,-6963.81 5590.25,-6927.9" />
|
||||
<polygon fill="black" stroke="black" points="5288.07,-6682.1 5298.74,-6679.57 5292.04,-6679.05 5296,-6676 5296,-6676 5296,-6676 5292.04,-6679.05 5293.26,-6672.43 5288.07,-6682.1 5288.07,-6682.1" />
|
||||
<ellipse fill="none" stroke="black" cx="5284.9" cy="-6684.54" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="5579.98,-6929 5583.87,-6919.78 5585.71,-6920.56 5581.82,-6929.77 5579.98,-6929" />
|
||||
<polyline fill="none" stroke="black" points="5581,-6924 5585.61,-6925.94 " />
|
||||
<polygon fill="black" stroke="black" points="5584.58,-6930.94 5588.47,-6921.73 5590.31,-6922.5 5586.43,-6931.72 5584.58,-6930.94" />
|
||||
<polyline fill="none" stroke="black" points="5585.61,-6925.94 5590.21,-6927.89 " />
|
||||
<text text-anchor="start" x="5356.5" y="-6978.8" font-family="Helvetica,sans-Serif" font-size="14.00">fk2lhcwpxlnqijr96irylrh1707</text>
|
||||
<path fill="none" stroke="black" d="M5281.52,-6867.14C5203.21,-6939.94 5215.41,-7155 5438.5,-7155 5666.43,-7155 5674.22,-7143.81 5590.25,-7107.9" />
|
||||
<polygon fill="black" stroke="black" points="5288.07,-6862.1 5298.74,-6859.57 5292.04,-6859.05 5296,-6856 5296,-6856 5296,-6856 5292.04,-6859.05 5293.26,-6852.43 5288.07,-6862.1 5288.07,-6862.1" />
|
||||
<ellipse fill="none" stroke="black" cx="5284.9" cy="-6864.54" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="5579.98,-7109 5583.87,-7099.78 5585.71,-7100.56 5581.82,-7109.77 5579.98,-7109" />
|
||||
<polyline fill="none" stroke="black" points="5581,-7104 5585.61,-7105.94 " />
|
||||
<polygon fill="black" stroke="black" points="5584.58,-7110.94 5588.47,-7101.73 5590.31,-7102.5 5586.43,-7111.72 5584.58,-7110.94" />
|
||||
<polyline fill="none" stroke="black" points="5585.61,-7105.94 5590.21,-7107.89 " />
|
||||
<text text-anchor="start" x="5356.5" y="-7158.8" font-family="Helvetica,sans-Serif" font-size="14.00">fk2lhcwpxlnqijr96irylrh1707</text>
|
||||
</g> <!-- reservedentry_1a7b8520 -->
|
||||
<g id="node40" class="node">
|
||||
<g id="node41" class="node">
|
||||
<title>
|
||||
reservedentry_1a7b8520
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5323,-7089 5323,-7108 5480,-7108 5480,-7089 5323,-7089" />
|
||||
<text text-anchor="start" x="5325" y="-7095.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."ReservedEntry"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5480,-7089 5480,-7108 5554,-7108 5554,-7089 5480,-7089" />
|
||||
<text text-anchor="start" x="5515" y="-7094.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5325" y="-7076.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="5451" y="-7075.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5482" y="-7075.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<text text-anchor="start" x="5325" y="-7056.8" font-family="Helvetica,sans-Serif" font-size="14.00">comment</text>
|
||||
<text text-anchor="start" x="5451" y="-7056.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5482" y="-7056.8" font-family="Helvetica,sans-Serif" font-size="14.00">text</text>
|
||||
<text text-anchor="start" x="5325" y="-7037.8" font-family="Helvetica,sans-Serif" font-size="14.00">reservation_type</text>
|
||||
<text text-anchor="start" x="5451" y="-7037.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5482" y="-7037.8" font-family="Helvetica,sans-Serif" font-size="14.00">int4 not null</text>
|
||||
<text text-anchor="start" x="5325" y="-7019.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">domain_label</text>
|
||||
<text text-anchor="start" x="5451" y="-7018.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5482" y="-7018.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5321.5,-7012.5 5321.5,-7109.5 5554.5,-7109.5 5554.5,-7012.5 5321.5,-7012.5" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5323,-7269 5323,-7288 5480,-7288 5480,-7269 5323,-7269" />
|
||||
<text text-anchor="start" x="5325" y="-7275.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."ReservedEntry"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5480,-7269 5480,-7288 5554,-7288 5554,-7269 5480,-7269" />
|
||||
<text text-anchor="start" x="5515" y="-7274.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5325" y="-7256.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="5451" y="-7255.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5482" y="-7255.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<text text-anchor="start" x="5325" y="-7236.8" font-family="Helvetica,sans-Serif" font-size="14.00">comment</text>
|
||||
<text text-anchor="start" x="5451" y="-7236.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5482" y="-7236.8" font-family="Helvetica,sans-Serif" font-size="14.00">text</text>
|
||||
<text text-anchor="start" x="5325" y="-7217.8" font-family="Helvetica,sans-Serif" font-size="14.00">reservation_type</text>
|
||||
<text text-anchor="start" x="5451" y="-7217.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5482" y="-7217.8" font-family="Helvetica,sans-Serif" font-size="14.00">int4 not null</text>
|
||||
<text text-anchor="start" x="5325" y="-7199.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">domain_label</text>
|
||||
<text text-anchor="start" x="5451" y="-7198.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5482" y="-7198.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5321.5,-7192.5 5321.5,-7289.5 5554.5,-7289.5 5554.5,-7192.5 5321.5,-7192.5" />
|
||||
</g> <!-- reservedlist_b97c3f1c -->
|
||||
<g id="node41" class="node">
|
||||
<g id="node42" class="node">
|
||||
<title>
|
||||
reservedlist_b97c3f1c
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4609,-7089 4609,-7108 4755,-7108 4755,-7089 4609,-7089" />
|
||||
<text text-anchor="start" x="4611" y="-7095.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."ReservedList"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4755,-7089 4755,-7108 4881,-7108 4881,-7089 4755,-7089" />
|
||||
<text text-anchor="start" x="4842" y="-7094.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4611" y="-7076.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4742" y="-7075.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4757" y="-7075.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="4742" y="-7056.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4757" y="-7056.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="4611" y="-7037.8" font-family="Helvetica,sans-Serif" font-size="14.00">creation_timestamp</text>
|
||||
<text text-anchor="start" x="4742" y="-7037.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4757" y="-7037.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz not null</text>
|
||||
<text text-anchor="start" x="4611" y="-7018.8" font-family="Helvetica,sans-Serif" font-size="14.00">name</text>
|
||||
<text text-anchor="start" x="4742" y="-7018.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4757" y="-7018.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4608,-7012.5 4608,-7109.5 4882,-7109.5 4882,-7012.5 4608,-7012.5" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4609,-7269 4609,-7288 4755,-7288 4755,-7269 4609,-7269" />
|
||||
<text text-anchor="start" x="4611" y="-7275.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."ReservedList"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4755,-7269 4755,-7288 4881,-7288 4881,-7269 4755,-7269" />
|
||||
<text text-anchor="start" x="4842" y="-7274.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4611" y="-7256.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4742" y="-7255.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4757" y="-7255.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="4742" y="-7236.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4757" y="-7236.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="4611" y="-7217.8" font-family="Helvetica,sans-Serif" font-size="14.00">creation_timestamp</text>
|
||||
<text text-anchor="start" x="4742" y="-7217.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4757" y="-7217.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz not null</text>
|
||||
<text text-anchor="start" x="4611" y="-7198.8" font-family="Helvetica,sans-Serif" font-size="14.00">name</text>
|
||||
<text text-anchor="start" x="4742" y="-7198.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4757" y="-7198.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="4608,-7192.5 4608,-7289.5 4882,-7289.5 4882,-7192.5 4608,-7192.5" />
|
||||
</g> <!-- reservedentry_1a7b8520->reservedlist_b97c3f1c -->
|
||||
<g id="edge65" class="edge">
|
||||
<title>
|
||||
reservedentry_1a7b8520:w->reservedlist_b97c3f1c:e
|
||||
</title>
|
||||
<path fill="none" stroke="black" d="M5303.81,-7080C5126.46,-7080 5073.26,-7080 4892.13,-7080" />
|
||||
<polygon fill="black" stroke="black" points="5312,-7080 5322,-7084.5 5317,-7080 5322,-7080 5322,-7080 5322,-7080 5317,-7080 5322,-7075.5 5312,-7080 5312,-7080" />
|
||||
<ellipse fill="none" stroke="black" cx="5308" cy="-7080" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="4883,-7085 4883,-7075 4885,-7075 4885,-7085 4883,-7085" />
|
||||
<polyline fill="none" stroke="black" points="4882,-7080 4887,-7080 " />
|
||||
<polygon fill="black" stroke="black" points="4888,-7085 4888,-7075 4890,-7075 4890,-7085 4888,-7085" />
|
||||
<polyline fill="none" stroke="black" points="4887,-7080 4892,-7080 " />
|
||||
<text text-anchor="start" x="4972" y="-7083.8" font-family="Helvetica,sans-Serif" font-size="14.00">fkgq03rk0bt1hb915dnyvd3vnfc</text>
|
||||
<path fill="none" stroke="black" d="M5303.81,-7260C5126.46,-7260 5073.26,-7260 4892.13,-7260" />
|
||||
<polygon fill="black" stroke="black" points="5312,-7260 5322,-7264.5 5317,-7260 5322,-7260 5322,-7260 5322,-7260 5317,-7260 5322,-7255.5 5312,-7260 5312,-7260" />
|
||||
<ellipse fill="none" stroke="black" cx="5308" cy="-7260" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="4883,-7265 4883,-7255 4885,-7255 4885,-7265 4883,-7265" />
|
||||
<polyline fill="none" stroke="black" points="4882,-7260 4887,-7260 " />
|
||||
<polygon fill="black" stroke="black" points="4888,-7265 4888,-7255 4890,-7255 4890,-7265 4888,-7265" />
|
||||
<polyline fill="none" stroke="black" points="4887,-7260 4892,-7260 " />
|
||||
<text text-anchor="start" x="4972" y="-7263.8" font-family="Helvetica,sans-Serif" font-size="14.00">fkgq03rk0bt1hb915dnyvd3vnfc</text>
|
||||
</g> <!-- serversecret_6cc90f09 -->
|
||||
<g id="node42" class="node">
|
||||
<g id="node43" class="node">
|
||||
<title>
|
||||
serversecret_6cc90f09
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5327,-7174 5327,-7193 5472,-7193 5472,-7174 5327,-7174" />
|
||||
<text text-anchor="start" x="5329" y="-7180.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."ServerSecret"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5472,-7174 5472,-7193 5549,-7193 5549,-7174 5472,-7174" />
|
||||
<text text-anchor="start" x="5510" y="-7179.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5329" y="-7160.8" font-family="Helvetica,sans-Serif" font-size="14.00">secret</text>
|
||||
<text text-anchor="start" x="5418" y="-7160.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5474" y="-7160.8" font-family="Helvetica,sans-Serif" font-size="14.00">uuid not null</text>
|
||||
<text text-anchor="start" x="5329" y="-7142.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">id</text>
|
||||
<text text-anchor="start" x="5418" y="-7141.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5474" y="-7141.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5326,-7135.5 5326,-7194.5 5550,-7194.5 5550,-7135.5 5326,-7135.5" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5327,-7354 5327,-7373 5472,-7373 5472,-7354 5327,-7354" />
|
||||
<text text-anchor="start" x="5329" y="-7360.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."ServerSecret"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5472,-7354 5472,-7373 5549,-7373 5549,-7354 5472,-7354" />
|
||||
<text text-anchor="start" x="5510" y="-7359.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5329" y="-7340.8" font-family="Helvetica,sans-Serif" font-size="14.00">secret</text>
|
||||
<text text-anchor="start" x="5418" y="-7340.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5474" y="-7340.8" font-family="Helvetica,sans-Serif" font-size="14.00">uuid not null</text>
|
||||
<text text-anchor="start" x="5329" y="-7322.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">id</text>
|
||||
<text text-anchor="start" x="5418" y="-7321.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5474" y="-7321.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5326,-7315.5 5326,-7374.5 5550,-7374.5 5550,-7315.5 5326,-7315.5" />
|
||||
</g> <!-- signedmarkrevocationentry_99c39721 -->
|
||||
<g id="node43" class="node">
|
||||
<g id="node44" class="node">
|
||||
<title>
|
||||
signedmarkrevocationentry_99c39721
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5252,-7279 5252,-7298 5498,-7298 5498,-7279 5252,-7279" />
|
||||
<text text-anchor="start" x="5254" y="-7285.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."SignedMarkRevocationEntry"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5498,-7279 5498,-7298 5624,-7298 5624,-7279 5498,-7279" />
|
||||
<text text-anchor="start" x="5585" y="-7284.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5254" y="-7266.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="5423" y="-7265.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5500" y="-7265.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<text text-anchor="start" x="5254" y="-7246.8" font-family="Helvetica,sans-Serif" font-size="14.00">revocation_time</text>
|
||||
<text text-anchor="start" x="5423" y="-7246.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5500" y="-7246.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz not null</text>
|
||||
<text text-anchor="start" x="5254" y="-7228.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">smd_id</text>
|
||||
<text text-anchor="start" x="5423" y="-7227.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5500" y="-7227.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5251,-7221 5251,-7299 5625,-7299 5625,-7221 5251,-7221" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5252,-7459 5252,-7478 5498,-7478 5498,-7459 5252,-7459" />
|
||||
<text text-anchor="start" x="5254" y="-7465.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."SignedMarkRevocationEntry"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5498,-7459 5498,-7478 5624,-7478 5624,-7459 5498,-7459" />
|
||||
<text text-anchor="start" x="5585" y="-7464.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5254" y="-7446.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="5423" y="-7445.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5500" y="-7445.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<text text-anchor="start" x="5254" y="-7426.8" font-family="Helvetica,sans-Serif" font-size="14.00">revocation_time</text>
|
||||
<text text-anchor="start" x="5423" y="-7426.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5500" y="-7426.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz not null</text>
|
||||
<text text-anchor="start" x="5254" y="-7408.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">smd_id</text>
|
||||
<text text-anchor="start" x="5423" y="-7407.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5500" y="-7407.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5251,-7401 5251,-7479 5625,-7479 5625,-7401 5251,-7401" />
|
||||
</g> <!-- signedmarkrevocationlist_c5d968fb -->
|
||||
<g id="node44" class="node">
|
||||
<g id="node45" class="node">
|
||||
<title>
|
||||
signedmarkrevocationlist_c5d968fb
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4572,-7279 4572,-7298 4808,-7298 4808,-7279 4572,-7279" />
|
||||
<text text-anchor="start" x="4574" y="-7285.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."SignedMarkRevocationList"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4808,-7279 4808,-7298 4918,-7298 4918,-7279 4808,-7279" />
|
||||
<text text-anchor="start" x="4879" y="-7284.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4574" y="-7266.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4731" y="-7265.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4810" y="-7265.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="4731" y="-7246.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4810" y="-7246.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="4574" y="-7227.8" font-family="Helvetica,sans-Serif" font-size="14.00">creation_time</text>
|
||||
<text text-anchor="start" x="4731" y="-7227.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4810" y="-7227.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz</text>
|
||||
<polygon fill="none" stroke="#888888" points="4571,-7221 4571,-7299 4919,-7299 4919,-7221 4571,-7221" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4572,-7459 4572,-7478 4808,-7478 4808,-7459 4572,-7459" />
|
||||
<text text-anchor="start" x="4574" y="-7465.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."SignedMarkRevocationList"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="4808,-7459 4808,-7478 4918,-7478 4918,-7459 4808,-7459" />
|
||||
<text text-anchor="start" x="4879" y="-7464.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="4574" y="-7446.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">revision_id</text>
|
||||
<text text-anchor="start" x="4731" y="-7445.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4810" y="-7445.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="4731" y="-7426.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4810" y="-7426.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="4574" y="-7407.8" font-family="Helvetica,sans-Serif" font-size="14.00">creation_time</text>
|
||||
<text text-anchor="start" x="4731" y="-7407.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="4810" y="-7407.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz</text>
|
||||
<polygon fill="none" stroke="#888888" points="4571,-7401 4571,-7479 4919,-7479 4919,-7401 4571,-7401" />
|
||||
</g> <!-- signedmarkrevocationentry_99c39721->signedmarkrevocationlist_c5d968fb -->
|
||||
<g id="edge66" class="edge">
|
||||
<title>
|
||||
signedmarkrevocationentry_99c39721:w->signedmarkrevocationlist_c5d968fb:e
|
||||
</title>
|
||||
<path fill="none" stroke="black" d="M5232.98,-7270C5103.39,-7270 5062.36,-7270 4929.17,-7270" />
|
||||
<polygon fill="black" stroke="black" points="5241,-7270 5251,-7274.5 5246,-7270 5251,-7270 5251,-7270 5251,-7270 5246,-7270 5251,-7265.5 5241,-7270 5241,-7270" />
|
||||
<ellipse fill="none" stroke="black" cx="5237" cy="-7270" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="4920,-7275 4920,-7265 4922,-7265 4922,-7275 4920,-7275" />
|
||||
<polyline fill="none" stroke="black" points="4919,-7270 4924,-7270 " />
|
||||
<polygon fill="black" stroke="black" points="4925,-7275 4925,-7265 4927,-7265 4927,-7275 4925,-7275" />
|
||||
<polyline fill="none" stroke="black" points="4924,-7270 4929,-7270 " />
|
||||
<text text-anchor="start" x="4981" y="-7273.8" font-family="Helvetica,sans-Serif" font-size="14.00">fk5ivlhvs3121yx2li5tqh54u4</text>
|
||||
<path fill="none" stroke="black" d="M5232.98,-7450C5103.39,-7450 5062.36,-7450 4929.17,-7450" />
|
||||
<polygon fill="black" stroke="black" points="5241,-7450 5251,-7454.5 5246,-7450 5251,-7450 5251,-7450 5251,-7450 5246,-7450 5251,-7445.5 5241,-7450 5241,-7450" />
|
||||
<ellipse fill="none" stroke="black" cx="5237" cy="-7450" rx="4" ry="4" />
|
||||
<polygon fill="black" stroke="black" points="4920,-7455 4920,-7445 4922,-7445 4922,-7455 4920,-7455" />
|
||||
<polyline fill="none" stroke="black" points="4919,-7450 4924,-7450 " />
|
||||
<polygon fill="black" stroke="black" points="4925,-7455 4925,-7445 4927,-7445 4927,-7455 4925,-7455" />
|
||||
<polyline fill="none" stroke="black" points="4924,-7450 4929,-7450 " />
|
||||
<text text-anchor="start" x="4981" y="-7453.8" font-family="Helvetica,sans-Serif" font-size="14.00">fk5ivlhvs3121yx2li5tqh54u4</text>
|
||||
</g> <!-- spec11threatmatch_a61228a6 -->
|
||||
<g id="node45" class="node">
|
||||
<g id="node46" class="node">
|
||||
<title>
|
||||
spec11threatmatch_a61228a6
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5288,-7478 5288,-7497 5478,-7497 5478,-7478 5288,-7478" />
|
||||
<text text-anchor="start" x="5290" y="-7484.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."Spec11ThreatMatch"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5478,-7478 5478,-7497 5588,-7497 5588,-7478 5478,-7478" />
|
||||
<text text-anchor="start" x="5549" y="-7483.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5290" y="-7465.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">id</text>
|
||||
<text text-anchor="start" x="5432" y="-7464.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5480" y="-7464.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="5432" y="-7445.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5480" y="-7445.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="5290" y="-7426.8" font-family="Helvetica,sans-Serif" font-size="14.00">check_date</text>
|
||||
<text text-anchor="start" x="5432" y="-7426.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5480" y="-7426.8" font-family="Helvetica,sans-Serif" font-size="14.00">date not null</text>
|
||||
<text text-anchor="start" x="5290" y="-7407.8" font-family="Helvetica,sans-Serif" font-size="14.00">domain_name</text>
|
||||
<text text-anchor="start" x="5432" y="-7407.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5480" y="-7407.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5290" y="-7388.8" font-family="Helvetica,sans-Serif" font-size="14.00">domain_repo_id</text>
|
||||
<text text-anchor="start" x="5432" y="-7388.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5480" y="-7388.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5290" y="-7369.8" font-family="Helvetica,sans-Serif" font-size="14.00">registrar_id</text>
|
||||
<text text-anchor="start" x="5432" y="-7369.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5480" y="-7369.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5290" y="-7350.8" font-family="Helvetica,sans-Serif" font-size="14.00">threat_types</text>
|
||||
<text text-anchor="start" x="5432" y="-7350.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5480" y="-7350.8" font-family="Helvetica,sans-Serif" font-size="14.00">_text not null</text>
|
||||
<text text-anchor="start" x="5290" y="-7331.8" font-family="Helvetica,sans-Serif" font-size="14.00">tld</text>
|
||||
<text text-anchor="start" x="5432" y="-7331.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5480" y="-7331.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5287,-7325.5 5287,-7498.5 5589,-7498.5 5589,-7325.5 5287,-7325.5" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5288,-7658 5288,-7677 5478,-7677 5478,-7658 5288,-7658" />
|
||||
<text text-anchor="start" x="5290" y="-7664.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."Spec11ThreatMatch"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5478,-7658 5478,-7677 5588,-7677 5588,-7658 5478,-7658" />
|
||||
<text text-anchor="start" x="5549" y="-7663.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5290" y="-7645.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">id</text>
|
||||
<text text-anchor="start" x="5432" y="-7644.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5480" y="-7644.8" font-family="Helvetica,sans-Serif" font-size="14.00">bigserial not null</text>
|
||||
<text text-anchor="start" x="5432" y="-7625.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5480" y="-7625.8" font-family="Helvetica,sans-Serif" font-size="14.00">auto-incremented</text>
|
||||
<text text-anchor="start" x="5290" y="-7606.8" font-family="Helvetica,sans-Serif" font-size="14.00">check_date</text>
|
||||
<text text-anchor="start" x="5432" y="-7606.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5480" y="-7606.8" font-family="Helvetica,sans-Serif" font-size="14.00">date not null</text>
|
||||
<text text-anchor="start" x="5290" y="-7587.8" font-family="Helvetica,sans-Serif" font-size="14.00">domain_name</text>
|
||||
<text text-anchor="start" x="5432" y="-7587.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5480" y="-7587.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5290" y="-7568.8" font-family="Helvetica,sans-Serif" font-size="14.00">domain_repo_id</text>
|
||||
<text text-anchor="start" x="5432" y="-7568.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5480" y="-7568.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5290" y="-7549.8" font-family="Helvetica,sans-Serif" font-size="14.00">registrar_id</text>
|
||||
<text text-anchor="start" x="5432" y="-7549.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5480" y="-7549.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5290" y="-7530.8" font-family="Helvetica,sans-Serif" font-size="14.00">threat_types</text>
|
||||
<text text-anchor="start" x="5432" y="-7530.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5480" y="-7530.8" font-family="Helvetica,sans-Serif" font-size="14.00">_text not null</text>
|
||||
<text text-anchor="start" x="5290" y="-7511.8" font-family="Helvetica,sans-Serif" font-size="14.00">tld</text>
|
||||
<text text-anchor="start" x="5432" y="-7511.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5480" y="-7511.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5287,-7505.5 5287,-7678.5 5589,-7678.5 5589,-7505.5 5287,-7505.5" />
|
||||
</g> <!-- tmchcrl_d282355 -->
|
||||
<g id="node47" class="node">
|
||||
<g id="node48" class="node">
|
||||
<title>
|
||||
tmchcrl_d282355
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5302,-7601 5302,-7620 5449,-7620 5449,-7601 5302,-7601" />
|
||||
<text text-anchor="start" x="5304" y="-7607.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."TmchCrl"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5449,-7601 5449,-7620 5575,-7620 5575,-7601 5449,-7601" />
|
||||
<text text-anchor="start" x="5536" y="-7606.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5304" y="-7587.8" font-family="Helvetica,sans-Serif" font-size="14.00">certificate_revocations</text>
|
||||
<text text-anchor="start" x="5443" y="-7587.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5451" y="-7587.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5304" y="-7568.8" font-family="Helvetica,sans-Serif" font-size="14.00">update_timestamp</text>
|
||||
<text text-anchor="start" x="5443" y="-7568.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5451" y="-7568.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz not null</text>
|
||||
<text text-anchor="start" x="5304" y="-7549.8" font-family="Helvetica,sans-Serif" font-size="14.00">url</text>
|
||||
<text text-anchor="start" x="5443" y="-7549.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5451" y="-7549.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5304" y="-7531.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">id</text>
|
||||
<text text-anchor="start" x="5443" y="-7530.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5451" y="-7530.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5300.5,-7524.5 5300.5,-7621.5 5575.5,-7621.5 5575.5,-7524.5 5300.5,-7524.5" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5302,-7781 5302,-7800 5449,-7800 5449,-7781 5302,-7781" />
|
||||
<text text-anchor="start" x="5304" y="-7787.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."TmchCrl"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5449,-7781 5449,-7800 5575,-7800 5575,-7781 5449,-7781" />
|
||||
<text text-anchor="start" x="5536" y="-7786.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5304" y="-7767.8" font-family="Helvetica,sans-Serif" font-size="14.00">certificate_revocations</text>
|
||||
<text text-anchor="start" x="5443" y="-7767.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5451" y="-7767.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5304" y="-7748.8" font-family="Helvetica,sans-Serif" font-size="14.00">update_timestamp</text>
|
||||
<text text-anchor="start" x="5443" y="-7748.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5451" y="-7748.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz not null</text>
|
||||
<text text-anchor="start" x="5304" y="-7729.8" font-family="Helvetica,sans-Serif" font-size="14.00">url</text>
|
||||
<text text-anchor="start" x="5443" y="-7729.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5451" y="-7729.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5304" y="-7711.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">id</text>
|
||||
<text text-anchor="start" x="5443" y="-7710.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5451" y="-7710.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<polygon fill="none" stroke="#888888" points="5300.5,-7704.5 5300.5,-7801.5 5575.5,-7801.5 5575.5,-7704.5 5300.5,-7704.5" />
|
||||
</g> <!-- userupdatehistory_24efd476 -->
|
||||
<g id="node49" class="node">
|
||||
<g id="node50" class="node">
|
||||
<title>
|
||||
userupdatehistory_24efd476
|
||||
</title>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5280,-7934 5280,-7953 5470,-7953 5470,-7934 5280,-7934" />
|
||||
<text text-anchor="start" x="5282" y="-7940.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."UserUpdateHistory"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5470,-7934 5470,-7953 5596,-7953 5596,-7934 5470,-7934" />
|
||||
<text text-anchor="start" x="5557" y="-7939.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5282" y="-7921.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">history_revision_id</text>
|
||||
<text text-anchor="start" x="5464" y="-7920.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7920.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<text text-anchor="start" x="5282" y="-7901.8" font-family="Helvetica,sans-Serif" font-size="14.00">history_modification_time</text>
|
||||
<text text-anchor="start" x="5464" y="-7901.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7901.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz not null</text>
|
||||
<text text-anchor="start" x="5282" y="-7882.8" font-family="Helvetica,sans-Serif" font-size="14.00">history_method</text>
|
||||
<text text-anchor="start" x="5464" y="-7882.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7882.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5282" y="-7863.8" font-family="Helvetica,sans-Serif" font-size="14.00">history_request_body</text>
|
||||
<text text-anchor="start" x="5464" y="-7863.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7863.8" font-family="Helvetica,sans-Serif" font-size="14.00">text</text>
|
||||
<text text-anchor="start" x="5282" y="-7844.8" font-family="Helvetica,sans-Serif" font-size="14.00">history_type</text>
|
||||
<text text-anchor="start" x="5464" y="-7844.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7844.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5282" y="-7825.8" font-family="Helvetica,sans-Serif" font-size="14.00">history_url</text>
|
||||
<text text-anchor="start" x="5464" y="-7825.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7825.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5282" y="-7806.8" font-family="Helvetica,sans-Serif" font-size="14.00">email_address</text>
|
||||
<text text-anchor="start" x="5464" y="-7806.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7806.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5282" y="-7787.8" font-family="Helvetica,sans-Serif" font-size="14.00">registry_lock_password_hash</text>
|
||||
<text text-anchor="start" x="5464" y="-7787.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7787.8" font-family="Helvetica,sans-Serif" font-size="14.00">text</text>
|
||||
<text text-anchor="start" x="5282" y="-7768.8" font-family="Helvetica,sans-Serif" font-size="14.00">registry_lock_password_salt</text>
|
||||
<text text-anchor="start" x="5464" y="-7768.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7768.8" font-family="Helvetica,sans-Serif" font-size="14.00">text</text>
|
||||
<text text-anchor="start" x="5282" y="-7749.8" font-family="Helvetica,sans-Serif" font-size="14.00">global_role</text>
|
||||
<text text-anchor="start" x="5464" y="-7749.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7749.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5282" y="-7730.8" font-family="Helvetica,sans-Serif" font-size="14.00">is_admin</text>
|
||||
<text text-anchor="start" x="5464" y="-7730.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7730.8" font-family="Helvetica,sans-Serif" font-size="14.00">bool not null</text>
|
||||
<text text-anchor="start" x="5282" y="-7711.8" font-family="Helvetica,sans-Serif" font-size="14.00">registrar_roles</text>
|
||||
<text text-anchor="start" x="5464" y="-7711.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7711.8" font-family="Helvetica,sans-Serif" font-size="14.00">hstore</text>
|
||||
<text text-anchor="start" x="5282" y="-7692.8" font-family="Helvetica,sans-Serif" font-size="14.00">update_timestamp</text>
|
||||
<text text-anchor="start" x="5464" y="-7692.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7692.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz</text>
|
||||
<text text-anchor="start" x="5282" y="-7673.8" font-family="Helvetica,sans-Serif" font-size="14.00">history_acting_user</text>
|
||||
<text text-anchor="start" x="5464" y="-7673.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7673.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5282" y="-7654.8" font-family="Helvetica,sans-Serif" font-size="14.00">registry_lock_email_address</text>
|
||||
<text text-anchor="start" x="5464" y="-7654.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7654.8" font-family="Helvetica,sans-Serif" font-size="14.00">text</text>
|
||||
<polygon fill="none" stroke="#888888" points="5279,-7648 5279,-7954 5597,-7954 5597,-7648 5279,-7648" />
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5280,-8114 5280,-8133 5470,-8133 5470,-8114 5280,-8114" />
|
||||
<text text-anchor="start" x="5282" y="-8120.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">public."UserUpdateHistory"</text>
|
||||
<polygon fill="#e9c2f2" stroke="transparent" points="5470,-8114 5470,-8133 5596,-8133 5596,-8114 5470,-8114" />
|
||||
<text text-anchor="start" x="5557" y="-8119.8" font-family="Helvetica,sans-Serif" font-size="14.00">[table]</text>
|
||||
<text text-anchor="start" x="5282" y="-8101.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">history_revision_id</text>
|
||||
<text text-anchor="start" x="5464" y="-8100.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-8100.8" font-family="Helvetica,sans-Serif" font-size="14.00">int8 not null</text>
|
||||
<text text-anchor="start" x="5282" y="-8081.8" font-family="Helvetica,sans-Serif" font-size="14.00">history_modification_time</text>
|
||||
<text text-anchor="start" x="5464" y="-8081.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-8081.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz not null</text>
|
||||
<text text-anchor="start" x="5282" y="-8062.8" font-family="Helvetica,sans-Serif" font-size="14.00">history_method</text>
|
||||
<text text-anchor="start" x="5464" y="-8062.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-8062.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5282" y="-8043.8" font-family="Helvetica,sans-Serif" font-size="14.00">history_request_body</text>
|
||||
<text text-anchor="start" x="5464" y="-8043.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-8043.8" font-family="Helvetica,sans-Serif" font-size="14.00">text</text>
|
||||
<text text-anchor="start" x="5282" y="-8024.8" font-family="Helvetica,sans-Serif" font-size="14.00">history_type</text>
|
||||
<text text-anchor="start" x="5464" y="-8024.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-8024.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5282" y="-8005.8" font-family="Helvetica,sans-Serif" font-size="14.00">history_url</text>
|
||||
<text text-anchor="start" x="5464" y="-8005.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-8005.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5282" y="-7986.8" font-family="Helvetica,sans-Serif" font-size="14.00">email_address</text>
|
||||
<text text-anchor="start" x="5464" y="-7986.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7986.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5282" y="-7967.8" font-family="Helvetica,sans-Serif" font-size="14.00">registry_lock_password_hash</text>
|
||||
<text text-anchor="start" x="5464" y="-7967.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7967.8" font-family="Helvetica,sans-Serif" font-size="14.00">text</text>
|
||||
<text text-anchor="start" x="5282" y="-7948.8" font-family="Helvetica,sans-Serif" font-size="14.00">registry_lock_password_salt</text>
|
||||
<text text-anchor="start" x="5464" y="-7948.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7948.8" font-family="Helvetica,sans-Serif" font-size="14.00">text</text>
|
||||
<text text-anchor="start" x="5282" y="-7929.8" font-family="Helvetica,sans-Serif" font-size="14.00">global_role</text>
|
||||
<text text-anchor="start" x="5464" y="-7929.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7929.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5282" y="-7910.8" font-family="Helvetica,sans-Serif" font-size="14.00">is_admin</text>
|
||||
<text text-anchor="start" x="5464" y="-7910.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7910.8" font-family="Helvetica,sans-Serif" font-size="14.00">bool not null</text>
|
||||
<text text-anchor="start" x="5282" y="-7891.8" font-family="Helvetica,sans-Serif" font-size="14.00">registrar_roles</text>
|
||||
<text text-anchor="start" x="5464" y="-7891.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7891.8" font-family="Helvetica,sans-Serif" font-size="14.00">hstore</text>
|
||||
<text text-anchor="start" x="5282" y="-7872.8" font-family="Helvetica,sans-Serif" font-size="14.00">update_timestamp</text>
|
||||
<text text-anchor="start" x="5464" y="-7872.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7872.8" font-family="Helvetica,sans-Serif" font-size="14.00">timestamptz</text>
|
||||
<text text-anchor="start" x="5282" y="-7853.8" font-family="Helvetica,sans-Serif" font-size="14.00">history_acting_user</text>
|
||||
<text text-anchor="start" x="5464" y="-7853.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7853.8" font-family="Helvetica,sans-Serif" font-size="14.00">text not null</text>
|
||||
<text text-anchor="start" x="5282" y="-7834.8" font-family="Helvetica,sans-Serif" font-size="14.00">registry_lock_email_address</text>
|
||||
<text text-anchor="start" x="5464" y="-7834.8" font-family="Helvetica,sans-Serif" font-size="14.00"> </text>
|
||||
<text text-anchor="start" x="5472" y="-7834.8" font-family="Helvetica,sans-Serif" font-size="14.00">text</text>
|
||||
<polygon fill="none" stroke="#888888" points="5279,-7828 5279,-8134 5597,-8134 5597,-7828 5279,-7828" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
@@ -9870,6 +9901,85 @@ td.section {
|
||||
</tbody>
|
||||
</table>
|
||||
<p> </p>
|
||||
<table>
|
||||
<caption style="background-color: #E9C2F2;">
|
||||
<span id="passwordresetrequest_8484e7b1" class="caption_name">public."PasswordResetRequest"</span> <span class="caption_description">[table]</span>
|
||||
</caption>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="spacer"></td>
|
||||
<td class="minwidth">type</td>
|
||||
<td class="minwidth">text not null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"></td>
|
||||
<td class="minwidth">request_time</td>
|
||||
<td class="minwidth">timestamptz not null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"></td>
|
||||
<td class="minwidth">requester</td>
|
||||
<td class="minwidth">text not null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"></td>
|
||||
<td class="minwidth">fulfillment_time</td>
|
||||
<td class="minwidth">timestamptz</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"></td>
|
||||
<td class="minwidth">destination_email</td>
|
||||
<td class="minwidth">text not null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"></td>
|
||||
<td class="minwidth"><b><i>verification_code</i></b></td>
|
||||
<td class="minwidth">text not null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"></td>
|
||||
<td class="minwidth">registrar_id</td>
|
||||
<td class="minwidth">text not null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3" class="section">Primary Key</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="name">"PasswordResetRequest_pkey"</td>
|
||||
<td class="description right">[primary key]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"></td>
|
||||
<td class="minwidth">verification_code</td>
|
||||
<td class="minwidth"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3" class="section">Indexes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="name">"PasswordResetRequest_pkey"</td>
|
||||
<td class="description right">[unique index]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"></td>
|
||||
<td class="minwidth">verification_code</td>
|
||||
<td class="minwidth">ascending</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p> </p>
|
||||
<table>
|
||||
<caption style="background-color: #E9C2F2;">
|
||||
<span id="pollmessage_614a523e" class="caption_name">public."PollMessage"</span> <span class="caption_description">[table]</span>
|
||||
|
||||
@@ -190,3 +190,5 @@ V189__remove_fk_consoleeppactionhistory.sql
|
||||
V190__remove_fk_registrarupdatehistory.sql
|
||||
V191__remove_fk_registrarpocupdatehistory.sql
|
||||
V192__add_last_poc_verification_date.sql
|
||||
V193__password_reset_request.sql
|
||||
V194__password_reset_request_registrar.sql
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
-- Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
--
|
||||
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||
-- you may not use this file except in compliance with the License.
|
||||
-- You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing, software
|
||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
|
||||
CREATE TABLE "PasswordResetRequest" (
|
||||
type text NOT NULL,
|
||||
request_time timestamptz NOT NULL,
|
||||
requester text NOT NULL,
|
||||
fulfillment_time timestamptz,
|
||||
destination_email text NOT NULL,
|
||||
verification_code text NOT NULL,
|
||||
PRIMARY KEY (verification_code)
|
||||
);
|
||||
@@ -0,0 +1,17 @@
|
||||
-- Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
--
|
||||
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||
-- you may not use this file except in compliance with the License.
|
||||
-- You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing, software
|
||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
|
||||
ALTER TABLE "PasswordResetRequest" ADD COLUMN registrar_id text;
|
||||
UPDATE "PasswordResetRequest" SET registrar_id = '' WHERE registrar_id IS NULL;
|
||||
ALTER TABLE "PasswordResetRequest" ALTER COLUMN registrar_id SET NOT NULL;
|
||||
@@ -646,6 +646,7 @@
|
||||
last_certificate_update_time timestamp(6) with time zone,
|
||||
last_expiring_cert_notification_sent_date timestamp(6) with time zone,
|
||||
last_expiring_failover_cert_notification_sent_date timestamp(6) with time zone,
|
||||
last_poc_verification_date timestamp(6) with time zone,
|
||||
localized_address_city text,
|
||||
localized_address_country_code text,
|
||||
localized_address_state text,
|
||||
|
||||
@@ -842,6 +842,21 @@ CREATE SEQUENCE public."Package_promotion_id_seq"
|
||||
ALTER SEQUENCE public."Package_promotion_id_seq" OWNED BY public."PackagePromotion".package_promotion_id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: PasswordResetRequest; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE public."PasswordResetRequest" (
|
||||
type text NOT NULL,
|
||||
request_time timestamp with time zone NOT NULL,
|
||||
requester text NOT NULL,
|
||||
fulfillment_time timestamp with time zone,
|
||||
destination_email text NOT NULL,
|
||||
verification_code text NOT NULL,
|
||||
registrar_id text NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: PollMessage; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
@@ -1682,6 +1697,14 @@ ALTER TABLE ONLY public."PackagePromotion"
|
||||
ADD CONSTRAINT "PackagePromotion_pkey" PRIMARY KEY (package_promotion_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: PasswordResetRequest PasswordResetRequest_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public."PasswordResetRequest"
|
||||
ADD CONSTRAINT "PasswordResetRequest_pkey" PRIMARY KEY (verification_code);
|
||||
|
||||
|
||||
--
|
||||
-- Name: PollMessage PollMessage_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
@@ -61,9 +61,6 @@ public enum RegistryEnvironment {
|
||||
/** Name of the environmental variable of the container name. */
|
||||
private static final String CONTAINER_ENV = "CONTAINER_NAME";
|
||||
|
||||
private static final boolean ON_JETTY =
|
||||
Boolean.parseBoolean(System.getProperty(JETTY_PROPERTY, "false"));
|
||||
|
||||
private static final boolean IS_CANARY =
|
||||
System.getenv().getOrDefault(CONTAINER_ENV, "").endsWith("-canary");
|
||||
|
||||
@@ -100,8 +97,9 @@ public enum RegistryEnvironment {
|
||||
return valueOf(Ascii.toUpperCase(System.getProperty(PROPERTY, UNITTEST.name())));
|
||||
}
|
||||
|
||||
// TODO(b/416299900): remove method after GAE is removed.
|
||||
public static boolean isOnJetty() {
|
||||
return ON_JETTY;
|
||||
return Boolean.parseBoolean(System.getProperty(JETTY_PROPERTY, "false"));
|
||||
}
|
||||
|
||||
public static boolean isCanary() {
|
||||
|
||||
Reference in New Issue
Block a user