mirror of
https://github.com/google/nomulus
synced 2026-08-03 22:06:13 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9437d8c72 | ||
|
|
19819444af | ||
|
|
15df3aea44 | ||
|
|
d000a5dff8 | ||
|
|
34694b4aef | ||
|
|
7ce7b23450 | ||
|
|
a5d1469281 |
@@ -15,12 +15,16 @@
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"builder": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"outputPath": "dist/console-webapp",
|
||||
"outputPath": {
|
||||
"base": "staged/dist/",
|
||||
"browser": ""
|
||||
},
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"polyfills": [
|
||||
"src/polyfills.ts"
|
||||
],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
@@ -34,7 +38,8 @@
|
||||
"stylePreprocessorOptions": {
|
||||
"includePaths": ["node_modules/"]
|
||||
},
|
||||
"scripts": []
|
||||
"scripts": [],
|
||||
"browser": "src/main.ts"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
@@ -59,9 +64,7 @@
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"optimization": false,
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true,
|
||||
"namedChunks": true
|
||||
|
||||
Generated
+2280
-1631
File diff suppressed because it is too large
Load Diff
+21
-21
@@ -4,7 +4,7 @@
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve --proxy-config dev-proxy.config.json",
|
||||
"build": "ng build --base-href=/console/ --output-path=staged/dist",
|
||||
"build": "ng build --base-href=/console/",
|
||||
"build:local": "ng build --base-href=/default/console/",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test --browsers=ChromeHeadless --watch=false",
|
||||
@@ -16,29 +16,29 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^17.3.5",
|
||||
"@angular/cdk": "^17.3.5",
|
||||
"@angular/common": "^17.3.5",
|
||||
"@angular/compiler": "^17.3.5",
|
||||
"@angular/core": "^17.3.5",
|
||||
"@angular/forms": "^17.3.5",
|
||||
"@angular/material": "^17.3.5",
|
||||
"@angular/platform-browser": "^17.3.5",
|
||||
"@angular/platform-browser-dynamic": "^17.3.5",
|
||||
"@angular/router": "^17.3.5",
|
||||
"@angular/animations": "^18.0.2",
|
||||
"@angular/cdk": "^18.0.2",
|
||||
"@angular/common": "^18.0.2",
|
||||
"@angular/compiler": "^18.0.2",
|
||||
"@angular/core": "^18.0.2",
|
||||
"@angular/forms": "^18.0.2",
|
||||
"@angular/material": "^18.0.2",
|
||||
"@angular/platform-browser": "^18.0.2",
|
||||
"@angular/platform-browser-dynamic": "^18.0.2",
|
||||
"@angular/router": "^18.0.2",
|
||||
"rxjs": "~7.5.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.14.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^17.3.5",
|
||||
"@angular-eslint/builder": "17.3.0",
|
||||
"@angular-eslint/eslint-plugin": "17.3.0",
|
||||
"@angular-eslint/eslint-plugin-template": "17.3.0",
|
||||
"@angular-eslint/schematics": "17.3.0",
|
||||
"@angular-eslint/template-parser": "17.3.0",
|
||||
"@angular/cli": "~17.3.5",
|
||||
"@angular/compiler-cli": "^17.3.5",
|
||||
"@angular-devkit/build-angular": "^18.0.3",
|
||||
"@angular-eslint/builder": "18.0.1",
|
||||
"@angular-eslint/eslint-plugin": "18.0.1",
|
||||
"@angular-eslint/eslint-plugin-template": "18.0.1",
|
||||
"@angular-eslint/schematics": "18.0.1",
|
||||
"@angular-eslint/template-parser": "18.0.1",
|
||||
"@angular/cli": "~18.0.3",
|
||||
"@angular/compiler-cli": "^18.0.2",
|
||||
"@types/jasmine": "~4.0.0",
|
||||
"@types/node": "^18.11.18",
|
||||
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
||||
@@ -52,6 +52,6 @@
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.0.0",
|
||||
"prettier": "2.8.7",
|
||||
"typescript": "~5.2.2"
|
||||
"typescript": "~5.4.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,25 +12,25 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
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 { RouterTestingModule } from '@angular/router/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { MaterialModule } from './material.module';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { BackendService } from './shared/services/backend.service';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
RouterTestingModule,
|
||||
MaterialModule,
|
||||
BrowserAnimationsModule,
|
||||
],
|
||||
providers: [BackendService],
|
||||
declarations: [AppComponent],
|
||||
imports: [RouterTestingModule, MaterialModule, BrowserAnimationsModule],
|
||||
providers: [
|
||||
BackendService,
|
||||
provideHttpClient(),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import { MaterialModule } from './material.module';
|
||||
|
||||
import { BackendService } from './shared/services/backend.service';
|
||||
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';
|
||||
import { BillingInfoComponent } from './billingInfo/billingInfo.component';
|
||||
import { DomainListComponent } from './domains/domainList.component';
|
||||
@@ -80,12 +80,12 @@ import { TldsComponent } from './tlds/tlds.component';
|
||||
WhoisComponent,
|
||||
WhoisEditComponent,
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [
|
||||
AppRoutingModule,
|
||||
BrowserAnimationsModule,
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
MaterialModule,
|
||||
SnackBarModule,
|
||||
],
|
||||
@@ -100,7 +100,7 @@ import { TldsComponent } from './tlds/tlds.component';
|
||||
subscriptSizing: 'dynamic',
|
||||
},
|
||||
},
|
||||
provideHttpClient(),
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DomainListComponent } from './domainList.component';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { DomainListComponent } from './domainList.component';
|
||||
|
||||
describe('DomainListComponent', () => {
|
||||
let component: DomainListComponent;
|
||||
@@ -27,12 +28,12 @@ describe('DomainListComponent', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [DomainListComponent],
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
MaterialModule,
|
||||
BrowserAnimationsModule,
|
||||
imports: [MaterialModule, BrowserAnimationsModule],
|
||||
providers: [
|
||||
BackendService,
|
||||
provideHttpClient(),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
providers: [BackendService],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DomainListComponent);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
max-width: 616px;
|
||||
|
||||
h2 {
|
||||
margin: 40px 0 25px 0;
|
||||
margin: 40px 0 25px 0 !important;
|
||||
}
|
||||
|
||||
section {
|
||||
|
||||
@@ -14,18 +14,24 @@
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RegistrarService } from './registrar.service';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
import { RegistrarService } from './registrar.service';
|
||||
|
||||
describe('RegistrarService', () => {
|
||||
let service: RegistrarService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule],
|
||||
providers: [BackendService, MatSnackBar],
|
||||
imports: [],
|
||||
providers: [
|
||||
BackendService,
|
||||
MatSnackBar,
|
||||
provideHttpClient(),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
});
|
||||
service = TestBed.inject(RegistrarService);
|
||||
});
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
<div class="console-app__registrar">
|
||||
<mat-form-field
|
||||
class="example-full-width"
|
||||
class="mat-form-field-density-5"
|
||||
appearance="outline"
|
||||
>
|
||||
<mat-form-field class="example-full-width" appearance="outline">
|
||||
<mat-label>Registrar</mat-label>
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RegistrarSelectorComponent } from './registrarSelector.component';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { RegistrarSelectorComponent } from './registrarSelector.component';
|
||||
|
||||
describe('RegistrarSelectorComponent', () => {
|
||||
let component: RegistrarSelectorComponent;
|
||||
@@ -26,13 +27,13 @@ describe('RegistrarSelectorComponent', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
MaterialModule,
|
||||
BrowserAnimationsModule,
|
||||
],
|
||||
providers: [BackendService],
|
||||
declarations: [RegistrarSelectorComponent],
|
||||
imports: [MaterialModule, BrowserAnimationsModule],
|
||||
providers: [
|
||||
BackendService,
|
||||
provideHttpClient(),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(RegistrarSelectorComponent);
|
||||
|
||||
@@ -14,12 +14,13 @@
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RegistrarComponent } from './registrarsTable.component';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
import { RegistrarComponent } from './registrarsTable.component';
|
||||
|
||||
describe('RegistrarComponent', () => {
|
||||
let component: RegistrarComponent;
|
||||
@@ -28,14 +29,12 @@ describe('RegistrarComponent', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [RegistrarComponent],
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
MaterialModule,
|
||||
BrowserAnimationsModule,
|
||||
],
|
||||
imports: [MaterialModule, BrowserAnimationsModule],
|
||||
providers: [
|
||||
BackendService,
|
||||
{ provide: ActivatedRoute, useValue: {} as ActivatedRoute },
|
||||
provideHttpClient(),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
|
||||
@@ -14,10 +14,11 @@
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import ContactComponent from './contact.component';
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { MaterialModule } from 'src/app/material.module';
|
||||
import { BackendService } from 'src/app/shared/services/backend.service';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import ContactComponent from './contact.component';
|
||||
|
||||
describe('ContactComponent', () => {
|
||||
let component: ContactComponent;
|
||||
@@ -26,8 +27,12 @@ describe('ContactComponent', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ContactComponent],
|
||||
imports: [HttpClientTestingModule, MaterialModule],
|
||||
providers: [BackendService],
|
||||
imports: [MaterialModule],
|
||||
providers: [
|
||||
BackendService,
|
||||
provideHttpClient(),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
}).compileComponents();
|
||||
fixture = TestBed.createComponent(ContactComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
@@ -26,6 +26,6 @@
|
||||
mat-form-field {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: 30px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,18 +14,19 @@
|
||||
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import SecurityComponent from './security.component';
|
||||
import { SecurityService, apiToUiConverter } from './security.service';
|
||||
import { BackendService } from 'src/app/shared/services/backend.service';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { MaterialModule } from 'src/app/material.module';
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { of } from 'rxjs';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MaterialModule } from 'src/app/material.module';
|
||||
import {
|
||||
Registrar,
|
||||
RegistrarService,
|
||||
} from 'src/app/registrar/registrar.service';
|
||||
import { BackendService } from 'src/app/shared/services/backend.service';
|
||||
import SecurityComponent from './security.component';
|
||||
import { SecurityService, apiToUiConverter } from './security.service';
|
||||
|
||||
describe('SecurityComponent', () => {
|
||||
let component: SecurityComponent;
|
||||
@@ -50,16 +51,13 @@ describe('SecurityComponent', () => {
|
||||
} as RegistrarService;
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
MaterialModule,
|
||||
BrowserAnimationsModule,
|
||||
FormsModule,
|
||||
],
|
||||
declarations: [SecurityComponent],
|
||||
imports: [MaterialModule, BrowserAnimationsModule, FormsModule],
|
||||
providers: [
|
||||
BackendService,
|
||||
{ provide: RegistrarService, useValue: dummyRegistrarService },
|
||||
provideHttpClient(),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
})
|
||||
.overrideComponent(SecurityComponent, {
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { BackendService } from 'src/app/shared/services/backend.service';
|
||||
import SecurityComponent from './security.component';
|
||||
import {
|
||||
SecurityService,
|
||||
SecuritySettings,
|
||||
@@ -21,10 +26,6 @@ import {
|
||||
apiToUiConverter,
|
||||
uiToApiConverter,
|
||||
} from './security.service';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import SecurityComponent from './security.component';
|
||||
import { BackendService } from 'src/app/shared/services/backend.service';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
|
||||
describe('SecurityService', () => {
|
||||
const uiMockData: SecuritySettings = {
|
||||
@@ -42,9 +43,15 @@ describe('SecurityService', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule],
|
||||
declarations: [SecurityComponent],
|
||||
providers: [MatSnackBar, SecurityService, BackendService],
|
||||
imports: [],
|
||||
providers: [
|
||||
MatSnackBar,
|
||||
SecurityService,
|
||||
BackendService,
|
||||
provideHttpClient(),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
});
|
||||
service = TestBed.inject(SecurityService);
|
||||
});
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
>Security</a
|
||||
>
|
||||
</nav>
|
||||
<mat-divider></mat-divider>
|
||||
<mat-tab-nav-panel #tabPanel>
|
||||
<router-outlet></router-outlet>
|
||||
</mat-tab-nav-panel>
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
.console-settings {
|
||||
> mat-divider {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.mdc-tab {
|
||||
&.active-link {
|
||||
border-bottom: 2px solid var(--primary);
|
||||
@@ -24,4 +21,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
nav {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin: 20px 0;
|
||||
margin-bottom: 20px;
|
||||
button {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -14,12 +14,13 @@
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import WhoisComponent from './whois.component';
|
||||
import { MaterialModule } from 'src/app/material.module';
|
||||
import { BackendService } from 'src/app/shared/services/backend.service';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { RegistrarService } from 'src/app/registrar/registrar.service';
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { MaterialModule } from 'src/app/material.module';
|
||||
import { RegistrarService } from 'src/app/registrar/registrar.service';
|
||||
import { BackendService } from 'src/app/shared/services/backend.service';
|
||||
import WhoisComponent from './whois.component';
|
||||
|
||||
describe('WhoisComponent', () => {
|
||||
let component: WhoisComponent;
|
||||
@@ -28,14 +29,12 @@ describe('WhoisComponent', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [WhoisComponent],
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
MaterialModule,
|
||||
BrowserAnimationsModule,
|
||||
],
|
||||
imports: [MaterialModule, BrowserAnimationsModule],
|
||||
providers: [
|
||||
BackendService,
|
||||
{ provide: RegistrarService, useValue: { registrar: {} } },
|
||||
provideHttpClient(),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
|
||||
@@ -30,14 +30,14 @@ body {
|
||||
|
||||
.console-app {
|
||||
.mat-headline-4 {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 24px;
|
||||
margin-top: 6px !important;
|
||||
margin-bottom: 24px !important;
|
||||
}
|
||||
.spacer {
|
||||
flex: 1;
|
||||
}
|
||||
h1 {
|
||||
margin-bottom: 24px;
|
||||
margin-bottom: 24px !important;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
|
||||
@@ -4,63 +4,19 @@
|
||||
|
||||
$secondary-color: #80868b;
|
||||
$border-color: #dadce0;
|
||||
$hue-undefined: "undefined";
|
||||
$blue-palette: (
|
||||
50: #e8f0fe,
|
||||
100: #d2e3fc,
|
||||
200: #aecbfa,
|
||||
300: #8ab4f8,
|
||||
400: #669df6,
|
||||
500: #4285f4,
|
||||
600: #1a73e8,
|
||||
700: #1967d2,
|
||||
800: #185abc,
|
||||
900: #174ea6,
|
||||
A100: $hue-undefined,
|
||||
A200: $hue-undefined,
|
||||
A400: $hue-undefined,
|
||||
A700: $hue-undefined,
|
||||
contrast: (
|
||||
50: #174ea6,
|
||||
100: #174ea6,
|
||||
200: #174ea6,
|
||||
300: #174ea6,
|
||||
400: #174ea6,
|
||||
500: white,
|
||||
600: white,
|
||||
700: white,
|
||||
800: white,
|
||||
900: white,
|
||||
A100: $hue-undefined,
|
||||
A200: $hue-undefined,
|
||||
A400: $hue-undefined,
|
||||
A700: $hue-undefined,
|
||||
),
|
||||
);
|
||||
|
||||
@function rem($valueInPixels, $rootbase: 16px) {
|
||||
@return math.div($valueInPixels, $rootbase) * 1rem;
|
||||
}
|
||||
|
||||
/** Copied from docs section **/
|
||||
|
||||
// Include the common styles for Angular Material. We include this here so that you only
|
||||
// have to load a single css file for Angular Material in your app.
|
||||
// Be sure that you only ever include this mixin once!
|
||||
@include mat.core();
|
||||
|
||||
// The warn palette is optional (defaults to red).
|
||||
$theme-warn: mat.define-palette(mat.$red-palette);
|
||||
|
||||
/**
|
||||
** Application specific section - Global styles and mixins
|
||||
**/
|
||||
|
||||
$theme-primary: mat.define-palette($blue-palette);
|
||||
|
||||
$typographyConfig: mat.define-typography-config(
|
||||
$typographyConfig: mat.m2-define-typography-config(
|
||||
$headline-1:
|
||||
mat.define-typography-level(
|
||||
mat.m2-define-typography-level(
|
||||
rem(29px),
|
||||
rem(36px),
|
||||
500,
|
||||
@@ -68,7 +24,7 @@ $typographyConfig: mat.define-typography-config(
|
||||
normal
|
||||
),
|
||||
$headline-4:
|
||||
mat.define-typography-level(
|
||||
mat.m2-define-typography-level(
|
||||
rem(28px),
|
||||
rem(36px),
|
||||
500,
|
||||
@@ -76,7 +32,7 @@ $typographyConfig: mat.define-typography-config(
|
||||
normal
|
||||
),
|
||||
$headline-5:
|
||||
mat.define-typography-level(
|
||||
mat.m2-define-typography-level(
|
||||
rem(20px),
|
||||
rem(28px),
|
||||
400,
|
||||
@@ -84,9 +40,15 @@ $typographyConfig: mat.define-typography-config(
|
||||
normal
|
||||
),
|
||||
$headline-6:
|
||||
mat.define-typography-level(rem(16px), rem(2px), 500, "Google Sans", normal),
|
||||
mat.m2-define-typography-level(
|
||||
rem(16px),
|
||||
rem(2px),
|
||||
500,
|
||||
"Google Sans",
|
||||
normal
|
||||
),
|
||||
$body-1:
|
||||
mat.define-typography-level(
|
||||
mat.m2-define-typography-level(
|
||||
rem(16px),
|
||||
rem(24px),
|
||||
400,
|
||||
@@ -94,7 +56,7 @@ $typographyConfig: mat.define-typography-config(
|
||||
normal
|
||||
),
|
||||
$body-2:
|
||||
mat.define-typography-level(
|
||||
mat.m2-define-typography-level(
|
||||
rem(14px),
|
||||
rem(20px),
|
||||
400,
|
||||
@@ -102,7 +64,7 @@ $typographyConfig: mat.define-typography-config(
|
||||
normal
|
||||
),
|
||||
$caption:
|
||||
mat.define-typography-level(
|
||||
mat.m2-define-typography-level(
|
||||
rem(14px),
|
||||
rem(24px),
|
||||
400,
|
||||
@@ -110,37 +72,13 @@ $typographyConfig: mat.define-typography-config(
|
||||
0.15px
|
||||
),
|
||||
$overline:
|
||||
mat.define-typography-level(rem(14px), rem(20px), 500, "Google Sans", 0.5px),
|
||||
);
|
||||
|
||||
@include mat.typography-hierarchy($typographyConfig);
|
||||
@mixin form-field-density($density) {
|
||||
$field-typography: mat.define-typography-config(
|
||||
$body-1: mat.define-typography-level(12px, 24px, 400),
|
||||
);
|
||||
@include mat.typography-level($field-typography, "body-1");
|
||||
@include mat.form-field-density($density);
|
||||
}
|
||||
|
||||
// Define lowest possible density class to be used in application
|
||||
// In the same manner -1...-5 classes can be defined
|
||||
.mat-form-field-density-5 {
|
||||
@include form-field-density(-5);
|
||||
}
|
||||
|
||||
/**
|
||||
** Light theme
|
||||
**/
|
||||
$light-theme: mat.define-light-theme(
|
||||
(
|
||||
color: (
|
||||
primary: $theme-primary,
|
||||
accent: $theme-primary,
|
||||
warn: $theme-warn,
|
||||
mat.m2-define-typography-level(
|
||||
rem(14px),
|
||||
rem(20px),
|
||||
500,
|
||||
"Google Sans",
|
||||
0.5px
|
||||
),
|
||||
density: 0,
|
||||
typography: $typographyConfig,
|
||||
)
|
||||
);
|
||||
|
||||
// Access and define a class with secondary color exposed
|
||||
@@ -165,12 +103,41 @@ mat-row:hover {
|
||||
|
||||
:root {
|
||||
--text: #5f6368;
|
||||
--primary: #{mat.get-color-from-palette($blue-palette, 500)};
|
||||
--lightest: #{mat.get-color-from-palette($blue-palette, 100)};
|
||||
--primary: #4285f4;
|
||||
--lightest: #d2e3fc;
|
||||
--light-highlight: #e8eaed;
|
||||
--lightest-highlight: #f8f9fa;
|
||||
--secondary: #{$secondary-color};
|
||||
--border: #{$border-color};
|
||||
--mat-tree-node-text-font: "Google Sans Text";
|
||||
--mat-tree-node-text-size: 0.95rem;
|
||||
--mat-sidenav-container-width: 280px;
|
||||
}
|
||||
|
||||
@include mat.all-component-themes($light-theme);
|
||||
$theme: mat.define-theme(
|
||||
(
|
||||
color: (
|
||||
theme-type: light,
|
||||
primary: mat.$blue-palette,
|
||||
),
|
||||
typography: (
|
||||
plain-family: "Google Sans",
|
||||
brand-family: "Google Sans Text",
|
||||
bold-weight: 600,
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
html {
|
||||
@include mat.all-component-themes($theme);
|
||||
@include mat.typography-hierarchy($typographyConfig);
|
||||
@include mat.form-field-theme(
|
||||
mat.define-theme(
|
||||
(
|
||||
density: (
|
||||
scale: -3,
|
||||
),
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist/out-tsc",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
@@ -12,7 +13,6 @@
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"downlevelIteration": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
|
||||
@@ -468,7 +468,7 @@ public class RdePipeline implements Serializable {
|
||||
HashSet<Serializable> contacts = new HashSet<>();
|
||||
contacts.add(domain.getAdminContact().getKey());
|
||||
contacts.add(domain.getTechContact().getKey());
|
||||
contacts.add(domain.getRegistrant().getKey());
|
||||
domain.getRegistrant().ifPresent(r -> contacts.add(r.getKey()));
|
||||
// Billing contact is not mandatory.
|
||||
if (domain.getBillingContact() != null) {
|
||||
contacts.add(domain.getBillingContact().getKey());
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.flows;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
@@ -37,6 +38,7 @@ import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.IsolationLevel;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Qualifier;
|
||||
@@ -191,6 +193,16 @@ public class FlowModule {
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FlowScope
|
||||
static JpaTransactionManager provideJpaTm(Class<? extends Flow> flowClass) {
|
||||
if (MutatingFlow.class.isAssignableFrom(flowClass)) {
|
||||
return tm();
|
||||
} else {
|
||||
return replicaTm();
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FlowScope
|
||||
static ResourceCommand provideResourceCommand(EppInput eppInput) {
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package google.registry.flows;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.xml.XmlTransformer.prettyPrint;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
@@ -28,6 +27,7 @@ import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.monitoring.whitebox.EppMetric;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
@@ -52,6 +52,8 @@ public class FlowRunner {
|
||||
@Inject SessionMetadata sessionMetadata;
|
||||
@Inject Trid trid;
|
||||
@Inject FlowReporter flowReporter;
|
||||
@Inject JpaTransactionManager jpaTransactionManager;
|
||||
|
||||
@Inject FlowRunner() {}
|
||||
|
||||
/** Runs the EPP flow, and records metrics on the given builder. */
|
||||
@@ -77,26 +79,24 @@ public class FlowRunner {
|
||||
return EppOutput.create(flowProvider.get().run());
|
||||
}
|
||||
try {
|
||||
// TODO(mcilwain/weiminyu): Use transactReadOnly() here for TransactionalFlow and transact()
|
||||
// for MutatingFlow.
|
||||
return tm().transact(
|
||||
isolationLevelOverride.orElse(null),
|
||||
() -> {
|
||||
try {
|
||||
EppOutput output = EppOutput.create(flowProvider.get().run());
|
||||
if (isDryRun) {
|
||||
throw new DryRunException(output);
|
||||
}
|
||||
if (flowClass.equals(LoginFlow.class)) {
|
||||
// In LoginFlow, registrarId isn't known until after the flow executes, so save
|
||||
// it then.
|
||||
eppMetricBuilder.setRegistrarId(sessionMetadata.getRegistrarId());
|
||||
}
|
||||
return output;
|
||||
} catch (EppException e) {
|
||||
throw new EppRuntimeException(e);
|
||||
}
|
||||
});
|
||||
return jpaTransactionManager.transact(
|
||||
isolationLevelOverride.orElse(null),
|
||||
() -> {
|
||||
try {
|
||||
EppOutput output = EppOutput.create(flowProvider.get().run());
|
||||
if (isDryRun) {
|
||||
throw new DryRunException(output);
|
||||
}
|
||||
if (flowClass.equals(LoginFlow.class)) {
|
||||
// In LoginFlow, registrarId isn't known until after the flow executes, so save
|
||||
// it then.
|
||||
eppMetricBuilder.setRegistrarId(sessionMetadata.getRegistrarId());
|
||||
}
|
||||
return output;
|
||||
} catch (EppException e) {
|
||||
throw new EppRuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (DryRunException e) {
|
||||
return e.output;
|
||||
} catch (EppRuntimeException e) {
|
||||
|
||||
@@ -412,11 +412,13 @@ public class DomainFlowUtils {
|
||||
|
||||
/** Verify that no linked resources have disallowed statuses. */
|
||||
static void verifyNotInPendingDelete(
|
||||
Set<DesignatedContact> contacts, VKey<Contact> registrant, Set<VKey<Host>> nameservers)
|
||||
Set<DesignatedContact> contacts,
|
||||
Optional<VKey<Contact>> registrant,
|
||||
Set<VKey<Host>> nameservers)
|
||||
throws EppException {
|
||||
ImmutableList.Builder<VKey<? extends EppResource>> keysToLoad = new ImmutableList.Builder<>();
|
||||
contacts.stream().map(DesignatedContact::getContactKey).forEach(keysToLoad::add);
|
||||
Optional.ofNullable(registrant).ifPresent(keysToLoad::add);
|
||||
registrant.ifPresent(keysToLoad::add);
|
||||
keysToLoad.addAll(nameservers);
|
||||
verifyNotInPendingDelete(EppResource.loadCached(keysToLoad.build()).values());
|
||||
}
|
||||
@@ -480,9 +482,10 @@ public class DomainFlowUtils {
|
||||
}
|
||||
|
||||
static void validateRequiredContactsPresent(
|
||||
@Nullable VKey<Contact> registrant, Set<DesignatedContact> contacts)
|
||||
Optional<VKey<Contact>> registrant, Set<DesignatedContact> contacts)
|
||||
throws RequiredParameterMissingException {
|
||||
if (registrant == null) {
|
||||
// TODO: Check minimum reg data set migration schedule here and don't throw when any are empty.
|
||||
if (registrant.isEmpty()) {
|
||||
throw new MissingRegistrantException();
|
||||
}
|
||||
|
||||
@@ -498,14 +501,14 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static void validateRegistrantAllowedOnTld(String tld, String registrantContactId)
|
||||
static void validateRegistrantAllowedOnTld(String tld, Optional<String> registrantContactId)
|
||||
throws RegistrantNotAllowedException {
|
||||
ImmutableSet<String> allowedRegistrants = Tld.get(tld).getAllowedRegistrantContactIds();
|
||||
// Empty allow list or null registrantContactId are ignored.
|
||||
if (registrantContactId != null
|
||||
if (registrantContactId.isPresent()
|
||||
&& !allowedRegistrants.isEmpty()
|
||||
&& !allowedRegistrants.contains(registrantContactId)) {
|
||||
throw new RegistrantNotAllowedException(registrantContactId);
|
||||
&& !allowedRegistrants.contains(registrantContactId.get())) {
|
||||
throw new RegistrantNotAllowedException(registrantContactId.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -119,8 +119,11 @@ public final class DomainInfoFlow implements TransactionalFlow {
|
||||
.setCreationTime(domain.getCreationTime())
|
||||
.setLastEppUpdateTime(domain.getLastEppUpdateTime())
|
||||
.setRegistrationExpirationTime(domain.getRegistrationExpirationTime())
|
||||
.setLastTransferTime(domain.getLastTransferTime())
|
||||
.setRegistrant(tm().loadByKey(domain.getRegistrant()).getContactId());
|
||||
.setLastTransferTime(domain.getLastTransferTime());
|
||||
domain
|
||||
.getRegistrant()
|
||||
.ifPresent(r -> infoBuilder.setRegistrant(tm().loadByKey(r).getContactId()));
|
||||
|
||||
// If authInfo is non-null, then the caller is authorized to see the full information since we
|
||||
// will have already verified the authInfo is valid.
|
||||
if (registrarId.equals(domain.getCurrentSponsorRegistrarId()) || authInfo.isPresent()) {
|
||||
|
||||
@@ -278,7 +278,7 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
.removeStatusValues(remove.getStatusValues())
|
||||
.removeContacts(remove.getContacts())
|
||||
.addContacts(add.getContacts())
|
||||
.setRegistrant(firstNonNull(change.getRegistrant(), domain.getRegistrant()))
|
||||
.setRegistrant(change.getRegistrant().or(domain::getRegistrant))
|
||||
.setAuthInfo(firstNonNull(change.getAuthInfo(), domain.getAuthInfo()));
|
||||
|
||||
if (!add.getNameservers().isEmpty()) {
|
||||
@@ -301,7 +301,10 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
}
|
||||
|
||||
private static void validateRegistrantIsntBeingRemoved(Change change) throws EppException {
|
||||
if (change.getRegistrantContactId() != null && change.getRegistrantContactId().isEmpty()) {
|
||||
// TODO(mcilwain): Make this check the minimum registration data set migration schedule
|
||||
// and not require presence of a registrant in later stages.
|
||||
if (change.getRegistrantContactId().isPresent()
|
||||
&& change.getRegistrantContactId().get().isEmpty()) {
|
||||
throw new MissingRegistrantException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
// Copyright 2024 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.common;
|
||||
|
||||
import static com.google.api.client.util.Preconditions.checkState;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Sets;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.CacheUtils;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@Entity
|
||||
public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
|
||||
/**
|
||||
* The current status of the feature the flag represents.
|
||||
*
|
||||
* <p>Currently, there is no enforced ordering of these status values, but that may change in the
|
||||
* future should new statuses be added to this enum that require it.
|
||||
*/
|
||||
public enum FeatureStatus {
|
||||
ACTIVE,
|
||||
INACTIVE
|
||||
}
|
||||
|
||||
/** The name of the flag/feature. */
|
||||
@Id String featureName;
|
||||
|
||||
/** A map of times for each {@link FeatureStatus} the FeatureFlag should hold. */
|
||||
@Column(nullable = false)
|
||||
TimedTransitionProperty<FeatureStatus> status =
|
||||
TimedTransitionProperty.withInitialValue(FeatureStatus.INACTIVE);
|
||||
|
||||
public static FeatureFlag get(String featureName) {
|
||||
FeatureFlag maybeFeatureFlag = CACHE.get(featureName);
|
||||
if (maybeFeatureFlag == null) {
|
||||
throw new FeatureFlagNotFoundException(featureName);
|
||||
} else {
|
||||
return maybeFeatureFlag;
|
||||
}
|
||||
}
|
||||
|
||||
public static ImmutableSet<FeatureFlag> get(Set<String> featureNames) {
|
||||
Map<String, FeatureFlag> featureFlags = CACHE.getAll(featureNames);
|
||||
ImmutableSet<String> missingFlags =
|
||||
Sets.difference(featureNames, featureFlags.keySet()).immutableCopy();
|
||||
if (missingFlags.isEmpty()) {
|
||||
return featureFlags.values().stream().collect(toImmutableSet());
|
||||
} else {
|
||||
throw new FeatureFlagNotFoundException(missingFlags);
|
||||
}
|
||||
}
|
||||
|
||||
/** A cache that loads the {@link FeatureFlag} for a given featureName. */
|
||||
private static final LoadingCache<String, FeatureFlag> CACHE =
|
||||
CacheUtils.newCacheBuilder(getSingletonCacheRefreshDuration())
|
||||
.build(
|
||||
new CacheLoader<>() {
|
||||
@Override
|
||||
public FeatureFlag load(final String featureName) {
|
||||
return tm().reTransact(() -> tm().loadByKeyIfPresent(createVKey(featureName)))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends String, ? extends FeatureFlag> loadAll(
|
||||
Set<? extends String> featureFlagNames) {
|
||||
ImmutableMap<String, VKey<FeatureFlag>> keysMap =
|
||||
featureFlagNames.stream()
|
||||
.collect(
|
||||
toImmutableMap(featureName -> featureName, FeatureFlag::createVKey));
|
||||
Map<VKey<? extends FeatureFlag>, FeatureFlag> entities =
|
||||
tm().reTransact(() -> tm().loadByKeysIfPresent(keysMap.values()));
|
||||
return entities.values().stream()
|
||||
.collect(toImmutableMap(flag -> flag.featureName, flag -> flag));
|
||||
}
|
||||
});
|
||||
|
||||
public static VKey<FeatureFlag> createVKey(String featureName) {
|
||||
return VKey.create(FeatureFlag.class, featureName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VKey<FeatureFlag> createVKey() {
|
||||
return createVKey(featureName);
|
||||
}
|
||||
|
||||
public String getFeatureName() {
|
||||
return featureName;
|
||||
}
|
||||
|
||||
public TimedTransitionProperty<FeatureStatus> getStatusMap() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public FeatureStatus getStatus(DateTime time) {
|
||||
return status.getValueAtTime(time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureFlag.Builder asBuilder() {
|
||||
return new FeatureFlag.Builder(clone(this));
|
||||
}
|
||||
|
||||
/** A builder for constructing {@link FeatureFlag} objects, since they are immutable. */
|
||||
public static class Builder extends Buildable.Builder<FeatureFlag> {
|
||||
|
||||
public Builder() {}
|
||||
|
||||
private Builder(FeatureFlag instance) {
|
||||
super(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureFlag build() {
|
||||
checkArgument(
|
||||
!Strings.isNullOrEmpty(getInstance().featureName),
|
||||
"Feature name must not be null or empty");
|
||||
getInstance().status.checkValidity();
|
||||
return super.build();
|
||||
}
|
||||
|
||||
public Builder setFeatureName(String featureName) {
|
||||
checkState(getInstance().featureName == null, "Feature name can only be set once");
|
||||
getInstance().featureName = featureName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setStatus(ImmutableSortedMap<DateTime, FeatureStatus> statusMap) {
|
||||
getInstance().status = TimedTransitionProperty.fromValueMap(statusMap);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/** Exception to throw when no FeatureFlag entity is found for given FeatureName string(s). */
|
||||
public static class FeatureFlagNotFoundException extends RuntimeException {
|
||||
|
||||
FeatureFlagNotFoundException(ImmutableSet<String> featureNames) {
|
||||
super("No feature flag object(s) found for " + Joiner.on(", ").join(featureNames));
|
||||
}
|
||||
|
||||
FeatureFlagNotFoundException(String featureName) {
|
||||
this(ImmutableSet.of(featureName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,7 +135,7 @@ public class DomainBase extends EppResource
|
||||
|
||||
@Expose VKey<Contact> billingContact;
|
||||
@Expose VKey<Contact> techContact;
|
||||
@Expose VKey<Contact> registrantContact;
|
||||
@Expose @Nullable VKey<Contact> registrantContact;
|
||||
|
||||
/** Authorization info (aka transfer secret) of the domain. */
|
||||
@Embedded
|
||||
@@ -585,8 +585,8 @@ public class DomainBase extends EppResource
|
||||
}
|
||||
|
||||
/** A key to the registrant who registered this domain. */
|
||||
public VKey<Contact> getRegistrant() {
|
||||
return registrantContact;
|
||||
public Optional<VKey<Contact>> getRegistrant() {
|
||||
return Optional.ofNullable(registrantContact);
|
||||
}
|
||||
|
||||
public VKey<Contact> getAdminContact() {
|
||||
@@ -606,6 +606,11 @@ public class DomainBase extends EppResource
|
||||
return getAllContacts(false);
|
||||
}
|
||||
|
||||
/** Gets all associated contacts for the domain, including the registrant. */
|
||||
public ImmutableSet<DesignatedContact> getAllContacts() {
|
||||
return getAllContacts(true);
|
||||
}
|
||||
|
||||
public DomainAuthInfo getAuthInfo() {
|
||||
return authInfo;
|
||||
}
|
||||
@@ -717,7 +722,6 @@ public class DomainBase extends EppResource
|
||||
instance.autorenewEndTime = firstNonNull(getInstance().autorenewEndTime, END_OF_TIME);
|
||||
|
||||
checkArgumentNotNull(emptyToNull(instance.domainName), "Missing domainName");
|
||||
checkArgumentNotNull(instance.getRegistrant(), "Missing registrant");
|
||||
instance.tld = getTldFromDomainName(instance.domainName);
|
||||
|
||||
T newDomain = super.build();
|
||||
@@ -749,9 +753,9 @@ public class DomainBase extends EppResource
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setRegistrant(VKey<Contact> registrant) {
|
||||
public B setRegistrant(Optional<VKey<Contact>> registrant) {
|
||||
// Set the registrant field specifically.
|
||||
getInstance().registrantContact = registrant;
|
||||
getInstance().registrantContact = registrant.orElse(null);
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ import google.registry.model.eppinput.ResourceCommand.ResourceUpdate;
|
||||
import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
@@ -76,21 +77,21 @@ public class DomainCommand {
|
||||
|
||||
/** The contactId of the registrant who registered this domain. */
|
||||
@XmlElement(name = "registrant")
|
||||
@Nullable
|
||||
String registrantContactId;
|
||||
|
||||
/** A resolved key to the registrant who registered this domain. */
|
||||
@XmlTransient VKey<Contact> registrant;
|
||||
@Nullable @XmlTransient VKey<Contact> registrant;
|
||||
|
||||
/** Authorization info (aka transfer secret) of the domain. */
|
||||
DomainAuthInfo authInfo;
|
||||
|
||||
public String getRegistrantContactId() {
|
||||
return registrantContactId;
|
||||
public Optional<String> getRegistrantContactId() {
|
||||
return Optional.ofNullable(registrantContactId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public VKey<Contact> getRegistrant() {
|
||||
return registrant;
|
||||
public Optional<VKey<Contact>> getRegistrant() {
|
||||
return Optional.ofNullable(registrant);
|
||||
}
|
||||
|
||||
public DomainAuthInfo getAuthInfo() {
|
||||
|
||||
@@ -63,6 +63,7 @@ public abstract class DomainInfoData implements ResponseData {
|
||||
abstract ImmutableSet<StatusValue> getStatusValues();
|
||||
|
||||
@XmlElement(name = "registrant")
|
||||
@Nullable
|
||||
abstract String getRegistrant();
|
||||
|
||||
@XmlElement(name = "contact")
|
||||
|
||||
@@ -41,9 +41,9 @@ 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.collect.Maps;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.collect.Range;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.CacheUtils;
|
||||
@@ -204,10 +204,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
public static ImmutableSet<Tld> get(Set<String> tlds) {
|
||||
Map<String, Tld> registries = CACHE.getAll(tlds);
|
||||
ImmutableSet<String> missingRegistries =
|
||||
registries.entrySet().stream()
|
||||
.filter(e -> e.getValue() == null)
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(toImmutableSet());
|
||||
Sets.difference(tlds, registries.keySet()).immutableCopy();
|
||||
if (missingRegistries.isEmpty()) {
|
||||
return registries.values().stream().collect(toImmutableSet());
|
||||
} else {
|
||||
@@ -243,7 +240,8 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
tlds.stream().collect(toImmutableMap(tld -> tld, Tld::createVKey));
|
||||
Map<VKey<? extends Tld>, Tld> entities =
|
||||
tm().reTransact(() -> tm().loadByKeysIfPresent(keysMap.values()));
|
||||
return Maps.transformEntries(keysMap, (k, v) -> entities.getOrDefault(v, null));
|
||||
return entities.values().stream()
|
||||
.collect(toImmutableMap(tld -> tld.tldStr, tld -> tld));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// Copyright 2024 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import google.registry.model.common.FeatureFlag.FeatureStatus;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/** JPA converter for {@link google.registry.model.common.FeatureFlag} status transitions. */
|
||||
@Converter(autoApply = true)
|
||||
public class FeatureFlagStatusConverter
|
||||
extends TimedTransitionPropertyConverterBase<FeatureStatus> {
|
||||
|
||||
@Override
|
||||
protected String convertValueToString(FeatureStatus value) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FeatureStatus convertStringToValue(String string) {
|
||||
return FeatureStatus.valueOf(string);
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ public class RdapEntityAction extends RdapActionBase {
|
||||
// they are global, and might have different roles for different domains.
|
||||
if (contact.isPresent() && isAuthorized(contact.get())) {
|
||||
return rdapJsonFormatter.createRdapContactEntity(
|
||||
contact.get(), ImmutableSet.of(), OutputDataType.FULL);
|
||||
contact, ImmutableSet.of(), OutputDataType.FULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -461,7 +461,7 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
.entitySearchResultsBuilder()
|
||||
.add(
|
||||
rdapJsonFormatter.createRdapContactEntity(
|
||||
contact, ImmutableSet.of(), outputDataType));
|
||||
Optional.of(contact), ImmutableSet.of(), outputDataType));
|
||||
newCursor =
|
||||
Optional.of(
|
||||
CONTACT_CURSOR_PREFIX
|
||||
|
||||
@@ -61,6 +61,7 @@ import google.registry.rdap.RdapDataStructures.RdapStatus;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapContactEntity;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapDomain;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapEntity;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapEntity.Role;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapNameserver;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapRegistrarEntity;
|
||||
import google.registry.rdap.RdapObjectClasses.SecureDns;
|
||||
@@ -74,6 +75,7 @@ import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -249,6 +251,16 @@ public class RdapJsonFormatter {
|
||||
private static final Ordering<DesignatedContact> DESIGNATED_CONTACT_ORDERING =
|
||||
Ordering.natural().onResultOf(DesignatedContact::getType);
|
||||
|
||||
/**
|
||||
* The list of RDAP contact roles that are required to be present on each domain.
|
||||
*
|
||||
* <p>Per RDAP Response Profile 2.7.3, A domain MUST have the REGISTRANT, ADMIN, TECH roles and
|
||||
* MAY have others. We also have the BILLING role in our system but it isn't required and is only
|
||||
* listed if actually present.
|
||||
*/
|
||||
private static final ImmutableSet<Role> REQUIRED_CONTACT_ROLES =
|
||||
ImmutableSet.of(Role.REGISTRANT, Role.ADMIN, Role.TECH);
|
||||
|
||||
/** Creates the TOS notice that is added to every reply. */
|
||||
Notice createTosNotice() {
|
||||
String linkValue = makeRdapServletRelativeUrl("help", RdapHelpAction.TOS_PATH);
|
||||
@@ -371,36 +383,48 @@ public class RdapJsonFormatter {
|
||||
// Load the registrant and other contacts and add them to the data.
|
||||
ImmutableMap<VKey<? extends Contact>, Contact> loadedContacts =
|
||||
replicaTm().transact(() -> replicaTm().loadByKeysIfPresent(domain.getReferencedContacts()));
|
||||
// RDAP Response Profile 2.7.3, A domain MUST have the REGISTRANT, ADMIN, TECH roles and MAY
|
||||
// have others. We also add the BILLING.
|
||||
//
|
||||
|
||||
// RDAP Response Profile 2.7.1, 2.7.3 - we MUST have the contacts. 2.7.4 discusses redaction of
|
||||
// fields we don't want to show (as opposed to not having contacts at all) because of GDPR etc.
|
||||
//
|
||||
// the GDPR redaction is handled in createRdapContactEntity
|
||||
// The GDPR redaction is handled in createRdapContactEntity.
|
||||
|
||||
// Load all contacts that are present and group them by type (it is common for a single contact
|
||||
// entity to be used across multiple contact types on domain, e.g. registrant and admin).
|
||||
ImmutableSetMultimap<VKey<Contact>, Type> contactsToRoles =
|
||||
Streams.concat(
|
||||
domain.getContacts().stream(),
|
||||
Stream.of(DesignatedContact.create(Type.REGISTRANT, domain.getRegistrant())))
|
||||
domain.getAllContacts().stream()
|
||||
.sorted(DESIGNATED_CONTACT_ORDERING)
|
||||
.collect(
|
||||
toImmutableSetMultimap(
|
||||
DesignatedContact::getContactKey, DesignatedContact::getType));
|
||||
|
||||
// Convert the contact entities to RDAP output contacts (this also converts the contact types
|
||||
// to RDAP roles).
|
||||
Set<RdapContactEntity> rdapContacts = new LinkedHashSet<>();
|
||||
for (VKey<Contact> contactKey : contactsToRoles.keySet()) {
|
||||
Set<RdapEntity.Role> roles =
|
||||
Set<Role> roles =
|
||||
contactsToRoles.get(contactKey).stream()
|
||||
.map(RdapJsonFormatter::convertContactTypeToRdapRole)
|
||||
.collect(toImmutableSet());
|
||||
if (roles.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
builder
|
||||
.entitiesBuilder()
|
||||
.add(
|
||||
createRdapContactEntity(
|
||||
loadedContacts.get(contactKey), roles, OutputDataType.INTERNAL));
|
||||
rdapContacts.add(
|
||||
createRdapContactEntity(
|
||||
Optional.ofNullable(loadedContacts.get(contactKey)), roles, OutputDataType.INTERNAL));
|
||||
}
|
||||
|
||||
// Loop through all required contact roles and fill in placeholder REDACTED info for any
|
||||
// required ones that are missing, i.e. because of minimum registration data set.
|
||||
for (Role role : REQUIRED_CONTACT_ROLES) {
|
||||
if (rdapContacts.stream().noneMatch(c -> c.roles().contains(role))) {
|
||||
rdapContacts.add(
|
||||
createRdapContactEntity(
|
||||
Optional.empty(), ImmutableSet.of(role), OutputDataType.INTERNAL));
|
||||
}
|
||||
}
|
||||
builder.entitiesBuilder().addAll(rdapContacts);
|
||||
|
||||
// Add the nameservers to the data; the load was kicked off above for efficiency.
|
||||
// RDAP Response Profile 2.9: we MUST have the nameservers
|
||||
for (Host host : HOST_RESOURCE_ORDERING.immutableSortedCopy(loadedHosts)) {
|
||||
@@ -502,25 +526,35 @@ public class RdapJsonFormatter {
|
||||
/**
|
||||
* Creates a JSON object for a {@link Contact} and associated contact type.
|
||||
*
|
||||
* <p>If the contact isn't present (i.e. because of minimum registration data set), then always
|
||||
* show all of its fields as if they were redacted, and always deny RDAP authorization.
|
||||
*
|
||||
* @param contact the contact resource object from which the JSON object should be created
|
||||
* @param roles the roles of this contact
|
||||
* @param outputDataType whether to generate full or summary data
|
||||
*/
|
||||
RdapContactEntity createRdapContactEntity(
|
||||
Contact contact, Iterable<RdapEntity.Role> roles, OutputDataType outputDataType) {
|
||||
Optional<Contact> contact, Iterable<RdapEntity.Role> roles, OutputDataType outputDataType) {
|
||||
RdapContactEntity.Builder contactBuilder = RdapContactEntity.builder();
|
||||
|
||||
// RDAP Response Profile 2.7.1, 2.7.3 - we MUST have the contacts. 2.7.4 discusses censoring of
|
||||
// fields we don't want to show (as opposed to not having contacts at all) because of GDPR etc.
|
||||
//
|
||||
// 2.8 allows for unredacted output for authorized people.
|
||||
// TODO(mcilwain): Once the RDAP profile is fully updated for minimum registration data set,
|
||||
// we will want to not include non-existent contacts at all, rather than
|
||||
// pretending they exist and just showing REDACTED info. This is especially
|
||||
// important for authorized flows, where you wouldn't expect to see redaction.
|
||||
boolean isAuthorized =
|
||||
rdapAuthorization.isAuthorizedForRegistrar(contact.getCurrentSponsorRegistrarId());
|
||||
contact.isPresent()
|
||||
&& rdapAuthorization.isAuthorizedForRegistrar(
|
||||
contact.get().getCurrentSponsorRegistrarId());
|
||||
|
||||
VcardArray.Builder vcardBuilder = VcardArray.builder();
|
||||
|
||||
if (isAuthorized) {
|
||||
fillRdapContactEntityWhenAuthorized(contactBuilder, vcardBuilder, contact, outputDataType);
|
||||
fillRdapContactEntityWhenAuthorized(
|
||||
contactBuilder, vcardBuilder, contact.get(), outputDataType);
|
||||
} else {
|
||||
// GTLD Registration Data Temp Spec 17may18, Appendix A, 2.3, 2.4 and RDAP Response Profile
|
||||
// 2.7.4.1, 2.7.4.2 - the following fields must be redacted:
|
||||
|
||||
@@ -20,7 +20,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.Domain;
|
||||
@@ -45,12 +44,11 @@ import google.registry.xjc.rgp.XjcRgpStatusType;
|
||||
import google.registry.xjc.rgp.XjcRgpStatusValueType;
|
||||
import google.registry.xjc.secdns.XjcSecdnsDsDataType;
|
||||
import google.registry.xjc.secdns.XjcSecdnsDsOrKeyType;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Utility class that turns {@link Domain} as {@link XjcRdeDomainElement}. */
|
||||
final class DomainToXjcConverter {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
/** Converts {@link Domain} to {@link XjcRdeDomainElement}. */
|
||||
static XjcRdeDomainElement convert(Domain domain, RdeMode mode) {
|
||||
return new XjcRdeDomainElement(convertDomain(domain, mode));
|
||||
@@ -168,11 +166,9 @@ final class DomainToXjcConverter {
|
||||
// o An OPTIONAL <registrant> element that contain the identifier for
|
||||
// the human or organizational social information object associated
|
||||
// as the holder of the domain name object.
|
||||
VKey<Contact> registrant = model.getRegistrant();
|
||||
if (registrant == null) {
|
||||
logger.atWarning().log("Domain %s has no registrant contact.", domainName);
|
||||
} else {
|
||||
Contact registrantContact = tm().transact(() -> tm().loadByKey(registrant));
|
||||
Optional<VKey<Contact>> registrant = model.getRegistrant();
|
||||
if (registrant.isPresent()) {
|
||||
Contact registrantContact = tm().transact(() -> tm().loadByKey(registrant.get()));
|
||||
checkState(
|
||||
registrantContact != null,
|
||||
"Registrant contact %s on domain %s does not exist",
|
||||
|
||||
@@ -254,5 +254,4 @@ public abstract class ConsoleApiAction implements Runnable {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-3
@@ -47,8 +47,7 @@ public class ConsoleUpdateRegistrarAction extends ConsoleApiAction {
|
||||
|
||||
@Inject
|
||||
ConsoleUpdateRegistrarAction(
|
||||
ConsoleApiParams consoleApiParams,
|
||||
@Parameter("registrar") Optional<Registrar> registrar) {
|
||||
ConsoleApiParams consoleApiParams, @Parameter("registrar") Optional<Registrar> registrar) {
|
||||
super(consoleApiParams);
|
||||
this.registrar = registrar;
|
||||
}
|
||||
@@ -108,5 +107,4 @@ public class ConsoleUpdateRegistrarAction extends ConsoleApiAction {
|
||||
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -105,7 +105,9 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
|
||||
"Registrar Abuse Contact Phone",
|
||||
abuseContact.map(RegistrarPoc::getPhoneNumber).orElse(""))
|
||||
.emitStatusValues(domain.getStatusValues(), domain.getGracePeriods())
|
||||
.emitContact("Registrant", Optional.of(domain.getRegistrant()), preferUnicode)
|
||||
// TODO(mcilwain): Investigate if the WHOIS spec requires us to always output REDACTED
|
||||
// text in WHOIS even if the contact is not present, and if so, do so.
|
||||
.emitContact("Registrant", domain.getRegistrant(), preferUnicode)
|
||||
.emitContact("Admin", getContactReference(Type.ADMIN), preferUnicode)
|
||||
.emitContact("Tech", getContactReference(Type.TECH), preferUnicode)
|
||||
.emitContact("Billing", getContactReference(Type.BILLING), preferUnicode)
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
<class>google.registry.model.contact.Contact</class>
|
||||
<class>google.registry.model.domain.Domain</class>
|
||||
<class>google.registry.model.domain.DomainHistory</class>
|
||||
<class>google.registry.model.common.FeatureFlag</class>
|
||||
<class>google.registry.model.domain.GracePeriod</class>
|
||||
<class>google.registry.model.domain.GracePeriod$GracePeriodHistory</class>
|
||||
<class>google.registry.model.domain.secdns.DomainDsData</class>
|
||||
@@ -97,6 +98,7 @@
|
||||
<class>google.registry.persistence.converter.CurrencyUnitConverter</class>
|
||||
<class>google.registry.persistence.converter.DateTimeConverter</class>
|
||||
<class>google.registry.persistence.converter.DurationConverter</class>
|
||||
<class>google.registry.persistence.converter.FeatureFlagStatusConverter</class>
|
||||
<class>google.registry.persistence.converter.IdnTableEnumSetConverter</class>
|
||||
<class>google.registry.persistence.converter.InetAddressSetConverter</class>
|
||||
<class>google.registry.persistence.converter.LocalDateConverter</class>
|
||||
|
||||
@@ -43,6 +43,7 @@ import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.util.Optional;
|
||||
import org.apache.beam.sdk.coders.StringUtf8Coder;
|
||||
import org.apache.beam.sdk.testing.PAssert;
|
||||
import org.apache.beam.sdk.values.PCollection;
|
||||
@@ -191,7 +192,7 @@ public class RegistryJpaReadTest {
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED,
|
||||
StatusValue.SERVER_RENEW_PROHIBITED,
|
||||
StatusValue.SERVER_HOLD))
|
||||
.setRegistrant(contact.createVKey())
|
||||
.setRegistrant(Optional.of(contact.createVKey()))
|
||||
.setContacts(ImmutableSet.of())
|
||||
.setSubordinateHosts(ImmutableSet.of("ns1.example.com"))
|
||||
.setPersistedCurrentSponsorRegistrarId(registrar.getRegistrarId())
|
||||
|
||||
@@ -31,6 +31,7 @@ import static google.registry.rde.RdeResourceType.HOST;
|
||||
import static google.registry.rde.RdeResourceType.REGISTRAR;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.insertSimpleResources;
|
||||
import static google.registry.testing.DatabaseHelper.newDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
@@ -83,10 +84,10 @@ import google.registry.rde.PendingDeposit;
|
||||
import google.registry.rde.RdeResourceType;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeKeyringModule;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -266,23 +267,23 @@ public class RdePipelineTest {
|
||||
persistHostHistory(host1);
|
||||
Domain helloDomain =
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("hello.soy", contact1)
|
||||
.asBuilder()
|
||||
.addNameserver(host1.createVKey())
|
||||
.build());
|
||||
newDomain("hello.soy", contact1).asBuilder().addNameserver(host1.createVKey()).build());
|
||||
persistDomainHistory(helloDomain);
|
||||
persistHostHistory(persistActiveHost("not-used-subordinate.hello.soy"));
|
||||
Host host2 = persistActiveHost("ns1.hello.soy");
|
||||
persistHostHistory(host2);
|
||||
|
||||
// This domain has no registrant.
|
||||
Domain kittyDomain =
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("kitty.fun", contact2)
|
||||
newDomain("kitty.fun", contact2)
|
||||
.asBuilder()
|
||||
.addNameservers(ImmutableSet.of(host1.createVKey(), host2.createVKey()))
|
||||
.setRegistrant(Optional.empty())
|
||||
.build());
|
||||
persistDomainHistory(kittyDomain);
|
||||
// Should not appear because the TLD is not included in a pending deposit.
|
||||
persistDomainHistory(persistEppResource(DatabaseHelper.newDomain("lol.cat", contact1)));
|
||||
persistDomainHistory(persistEppResource(newDomain("lol.cat", contact1)));
|
||||
// To be deleted.
|
||||
Domain deletedDomain = persistActiveDomain("deleted.soy");
|
||||
persistDomainHistory(deletedDomain);
|
||||
@@ -325,7 +326,7 @@ public class RdePipelineTest {
|
||||
persistHostHistory(futureHost);
|
||||
persistDomainHistory(
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("future.soy", futureContact)
|
||||
newDomain("future.soy", futureContact)
|
||||
.asBuilder()
|
||||
.setNameservers(futureHost.createVKey())
|
||||
.build()));
|
||||
@@ -379,18 +380,30 @@ public class RdePipelineTest {
|
||||
// The same registrars are attached to all the pending deposits.
|
||||
.containsExactly("New Registrar", "The Registrar", "external_monitoring");
|
||||
// Domain fragments.
|
||||
if ("soy".equals(kv.getKey().tld())) {
|
||||
assertThat(
|
||||
getFragmentForType(kv, DOMAIN)
|
||||
.map(getXmlElement(DOMAIN_NAME_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("hello.soy");
|
||||
} else {
|
||||
assertThat(
|
||||
getFragmentForType(kv, DOMAIN)
|
||||
.map(getXmlElement(DOMAIN_NAME_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("cat.fun");
|
||||
ImmutableSet<DepositFragment> domainFrags =
|
||||
getFragmentForType(kv, DOMAIN).collect(toImmutableSet());
|
||||
assertThat(domainFrags).hasSize(1);
|
||||
if ("fun".equals(kv.getKey().tld())) {
|
||||
// Note that this fragment contains no registrant (which is valid).
|
||||
assertThat(domainFrags.stream().findFirst().get().xml().strip())
|
||||
.isEqualTo(
|
||||
"""
|
||||
<rdeDomain:domain>
|
||||
<rdeDomain:name>cat.fun</rdeDomain:name>
|
||||
<rdeDomain:roid>15-FUN</rdeDomain:roid>
|
||||
<rdeDomain:uName>cat.fun</rdeDomain:uName>
|
||||
<rdeDomain:status s="ok"/>
|
||||
<rdeDomain:contact type="admin">contact456</rdeDomain:contact>
|
||||
<rdeDomain:contact type="tech">contact456</rdeDomain:contact>
|
||||
<rdeDomain:ns>
|
||||
<domain:hostObj>ns1.external.tld</domain:hostObj>
|
||||
<domain:hostObj>ns1.hello.soy</domain:hostObj>
|
||||
</rdeDomain:ns>
|
||||
<rdeDomain:clID>TheRegistrar</rdeDomain:clID>
|
||||
<rdeDomain:crRr>TheRegistrar</rdeDomain:crRr>
|
||||
<rdeDomain:crDate>1970-01-01T00:00:00Z</rdeDomain:crDate>
|
||||
<rdeDomain:exDate>294247-01-10T04:00:54Z</rdeDomain:exDate>
|
||||
</rdeDomain:domain>""");
|
||||
}
|
||||
if (kv.getKey().mode().equals(FULL)) {
|
||||
// Contact fragments for hello.soy.
|
||||
@@ -400,12 +413,35 @@ public class RdePipelineTest {
|
||||
.map(getXmlElement(CONTACT_ID_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("contact1234", "contact789");
|
||||
|
||||
// Host fragments for hello.soy.
|
||||
assertThat(
|
||||
getFragmentForType(kv, HOST)
|
||||
.map(getXmlElement(HOST_NAME_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("ns1.external.tld", "ns1.lol.cat");
|
||||
|
||||
// Domain fragments for hello.soy: Note that this contains a registrant.
|
||||
assertThat(domainFrags.stream().findFirst().get().xml().strip())
|
||||
.isEqualTo(
|
||||
"""
|
||||
<rdeDomain:domain>
|
||||
<rdeDomain:name>hello.soy</rdeDomain:name>
|
||||
<rdeDomain:roid>E-SOY</rdeDomain:roid>
|
||||
<rdeDomain:uName>hello.soy</rdeDomain:uName>
|
||||
<rdeDomain:status s="ok"/>
|
||||
<rdeDomain:registrant>contact1234</rdeDomain:registrant>
|
||||
<rdeDomain:contact type="admin">contact789</rdeDomain:contact>
|
||||
<rdeDomain:contact type="tech">contact1234</rdeDomain:contact>
|
||||
<rdeDomain:ns>
|
||||
<domain:hostObj>ns1.external.tld</domain:hostObj>
|
||||
<domain:hostObj>ns1.lol.cat</domain:hostObj>
|
||||
</rdeDomain:ns>
|
||||
<rdeDomain:clID>TheRegistrar</rdeDomain:clID>
|
||||
<rdeDomain:crRr>TheRegistrar</rdeDomain:crRr>
|
||||
<rdeDomain:crDate>1970-01-01T00:00:00Z</rdeDomain:crDate>
|
||||
<rdeDomain:exDate>294247-01-10T04:00:54Z</rdeDomain:exDate>
|
||||
</rdeDomain:domain>""");
|
||||
} else {
|
||||
// Contact fragments for cat.fun.
|
||||
assertThat(
|
||||
@@ -413,6 +449,7 @@ public class RdePipelineTest {
|
||||
.map(getXmlElement(CONTACT_ID_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("contactABC");
|
||||
|
||||
// Host fragments for cat.soy.
|
||||
assertThat(
|
||||
getFragmentForType(kv, HOST)
|
||||
@@ -429,6 +466,25 @@ public class RdePipelineTest {
|
||||
fragment.type().equals(CONTACT)
|
||||
|| fragment.type().equals(HOST)))
|
||||
.isFalse();
|
||||
|
||||
// Domain fragments for hello.soy: Note that this contains no contact info.
|
||||
assertThat(domainFrags.stream().findFirst().get().xml().strip())
|
||||
.isEqualTo(
|
||||
"""
|
||||
<rdeDomain:domain>
|
||||
<rdeDomain:name>hello.soy</rdeDomain:name>
|
||||
<rdeDomain:roid>E-SOY</rdeDomain:roid>
|
||||
<rdeDomain:uName>hello.soy</rdeDomain:uName>
|
||||
<rdeDomain:status s="ok"/>
|
||||
<rdeDomain:ns>
|
||||
<domain:hostObj>ns1.external.tld</domain:hostObj>
|
||||
<domain:hostObj>ns1.lol.cat</domain:hostObj>
|
||||
</rdeDomain:ns>
|
||||
<rdeDomain:clID>TheRegistrar</rdeDomain:clID>
|
||||
<rdeDomain:crRr>TheRegistrar</rdeDomain:crRr>
|
||||
<rdeDomain:crDate>1970-01-01T00:00:00Z</rdeDomain:crDate>
|
||||
<rdeDomain:exDate>294247-01-10T04:00:54Z</rdeDomain:exDate>
|
||||
</rdeDomain:domain>""");
|
||||
}
|
||||
});
|
||||
return null;
|
||||
|
||||
@@ -54,6 +54,7 @@ import google.registry.util.Retrier;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import org.apache.beam.sdk.coders.KvCoder;
|
||||
import org.apache.beam.sdk.coders.SerializableCoder;
|
||||
import org.apache.beam.sdk.options.PipelineOptionsFactory;
|
||||
@@ -297,7 +298,7 @@ class Spec11PipelineTest {
|
||||
.setLastEppUpdateTime(fakeClock.nowUtc())
|
||||
.setLastEppUpdateRegistrarId(registrar.getRegistrarId())
|
||||
.setLastTransferTime(fakeClock.nowUtc())
|
||||
.setRegistrant(contact.createVKey())
|
||||
.setRegistrant(Optional.of(contact.createVKey()))
|
||||
.setPersistedCurrentSponsorRegistrarId(registrar.getRegistrarId())
|
||||
.setRegistrationExpirationTime(fakeClock.nowUtc().plusYears(1))
|
||||
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("password")))
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright 2024 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.flows;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.newTld;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import dagger.Component;
|
||||
import google.registry.flows.FlowComponent.FlowComponentModule;
|
||||
import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.persistence.transaction.DatabaseException;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import google.registry.testing.EppLoader;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link FlowModule}. */
|
||||
public class FlowModuleTest {
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
private EppInput getEppInput(String eppInputXmlFilename) throws EppException {
|
||||
return new EppLoader(this, eppInputXmlFilename).getEpp();
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenMutatingFlow_thenPrimaryTmIsUsed() throws EppException {
|
||||
String eppInputXmlFilename = "domain_create.xml";
|
||||
FlowModule flowModule =
|
||||
new FlowModule.Builder().setEppInput(getEppInput(eppInputXmlFilename)).build();
|
||||
JpaTransactionManager tm =
|
||||
DaggerFlowModuleTest_FlowModuleTestComponent.builder()
|
||||
.flowModule(flowModule)
|
||||
.build()
|
||||
.jpaTm();
|
||||
assertThat(tm).isEqualTo(tm());
|
||||
tm.transact(() -> tm.put(newTld("app", "ROID")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenNonMutatingFlow_thenReplicaTmIsUsed() throws EppException {
|
||||
String eppInputXmlFilename = "domain_info.xml";
|
||||
FlowModule flowModule =
|
||||
new FlowModule.Builder().setEppInput(getEppInput(eppInputXmlFilename)).build();
|
||||
JpaTransactionManager tm =
|
||||
DaggerFlowModuleTest_FlowModuleTestComponent.builder()
|
||||
.flowModule(flowModule)
|
||||
.build()
|
||||
.jpaTm();
|
||||
assertThat(tm).isEqualTo(replicaTm());
|
||||
assertThat(
|
||||
assertThrows(
|
||||
DatabaseException.class, () -> tm.transact(() -> tm.put(newTld("app", "ROID")))))
|
||||
.hasMessageThat()
|
||||
.contains("cannot execute INSERT in a read-only transaction");
|
||||
}
|
||||
|
||||
@FlowScope
|
||||
@Component(modules = {FlowModule.class, FlowComponentModule.class})
|
||||
public interface FlowModuleTestComponent {
|
||||
JpaTransactionManager jpaTm();
|
||||
|
||||
@Component.Builder
|
||||
interface Builder {
|
||||
Builder flowModule(FlowModule flowModule);
|
||||
|
||||
FlowModuleTestComponent build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,6 +111,7 @@ class FlowRunnerTest {
|
||||
new StatelessRequestSessionMetadata("TheRegistrar", ImmutableSet.of());
|
||||
flowRunner.trid = Trid.create("client-123", "server-456");
|
||||
flowRunner.flowReporter = mock(FlowReporter.class);
|
||||
flowRunner.jpaTransactionManager = tm();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -103,6 +103,7 @@ import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
@@ -162,7 +163,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setCreationTimeForTest(TIME_BEFORE_FLOW)
|
||||
.setRegistrant(contact.createVKey())
|
||||
.setRegistrant(Optional.of(contact.createVKey()))
|
||||
.setRegistrationExpirationTime(expirationTime)
|
||||
.build());
|
||||
earlierHistoryEntry =
|
||||
@@ -738,7 +739,8 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
DatabaseHelper.newDomain("example1.tld")
|
||||
.asBuilder()
|
||||
.setRegistrant(
|
||||
loadByForeignKey(Contact.class, "sh8013", clock.nowUtc()).get().createVKey())
|
||||
Optional.of(
|
||||
loadByForeignKey(Contact.class, "sh8013", clock.nowUtc()).get().createVKey()))
|
||||
.setNameservers(ImmutableSet.of(host.createVKey()))
|
||||
.setDeletionTime(START_OF_TIME)
|
||||
.build());
|
||||
|
||||
@@ -78,6 +78,7 @@ import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.JpaTransactionManagerExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.xml.ValidationMode;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.money.Money;
|
||||
@@ -139,7 +140,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
.setLastEppUpdateTime(DateTime.parse("1999-12-03T09:00:00.0Z"))
|
||||
.setLastTransferTime(DateTime.parse("2000-04-08T09:00:00.0Z"))
|
||||
.setRegistrationExpirationTime(DateTime.parse("2005-04-03T22:00:00.0Z"))
|
||||
.setRegistrant(registrant.createVKey())
|
||||
.setRegistrant(Optional.of(registrant.createVKey()))
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, contact.createVKey()),
|
||||
@@ -227,6 +228,13 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
doSuccessfulTest("domain_info_response.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_noRegistrant() throws Exception {
|
||||
persistTestEntities(false);
|
||||
domain = persistResource(domain.asBuilder().setRegistrant(Optional.empty()).build());
|
||||
doSuccessfulTest("domain_info_response_no_registrant.xml", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_clTridNotSpecified() throws Exception {
|
||||
setEppInput("domain_info_no_cltrid.xml");
|
||||
|
||||
@@ -162,7 +162,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
DesignatedContact.create(Type.TECH, mak21Contact.createVKey()),
|
||||
DesignatedContact.create(Type.ADMIN, mak21Contact.createVKey()),
|
||||
DesignatedContact.create(Type.BILLING, mak21Contact.createVKey())))
|
||||
.setRegistrant(mak21Contact.createVKey())
|
||||
.setRegistrant(Optional.of(mak21Contact.createVKey()))
|
||||
.setNameservers(ImmutableSet.of(host.createVKey()))
|
||||
.build());
|
||||
persistResource(
|
||||
@@ -338,7 +338,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
.asBuilder()
|
||||
.setNameservers(nameservers.build())
|
||||
.setContacts(ImmutableSet.copyOf(contacts.subList(0, 3)))
|
||||
.setRegistrant(contacts.get(3).getContactKey())
|
||||
.setRegistrant(Optional.of(contacts.get(3).getContactKey()))
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
assertMutatingFlow(true);
|
||||
@@ -348,7 +348,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
assertThat(domain.getNameservers()).hasSize(13);
|
||||
// getContacts does not return contacts of type REGISTRANT, so check these separately.
|
||||
assertThat(domain.getContacts()).hasSize(3);
|
||||
assertThat(loadByKey(domain.getRegistrant()).getContactId()).isEqualTo("max_test_7");
|
||||
assertThat(loadByKey(domain.getRegistrant().get()).getContactId()).isEqualTo("max_test_7");
|
||||
assertNoBillingEvents();
|
||||
assertDomainDnsRequests("example.tld");
|
||||
}
|
||||
@@ -426,7 +426,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setRegistrant(sh8013.createVKey())
|
||||
.setRegistrant(Optional.of(sh8013.createVKey()))
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
@@ -441,7 +441,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setRegistrant(sh8013Key)
|
||||
.setRegistrant(Optional.of(sh8013Key))
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, sh8013Key),
|
||||
@@ -1590,7 +1590,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
.setAllowedFullyQualifiedHostNames(ImmutableSet.of("ns1.example.foo"))
|
||||
.build());
|
||||
runFlow();
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant()).getContactId())
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant().get()).getContactId())
|
||||
.isEqualTo("sh8013");
|
||||
}
|
||||
|
||||
@@ -1605,7 +1605,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
.forEach(
|
||||
contact ->
|
||||
assertThat(loadByKey(contact.getContactKey()).getContactId()).isEqualTo("mak21"));
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant()).getContactId())
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant().get()).getContactId())
|
||||
.isEqualTo("mak21");
|
||||
|
||||
runFlow();
|
||||
@@ -1615,7 +1615,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
.forEach(
|
||||
contact ->
|
||||
assertThat(loadByKey(contact.getContactKey()).getContactId()).isEqualTo("sh8013"));
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant()).getContactId())
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant().get()).getContactId())
|
||||
.isEqualTo("sh8013");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
// Copyright 2024 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.common;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
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 com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.EntityTestCase;
|
||||
import google.registry.model.common.FeatureFlag.FeatureFlagNotFoundException;
|
||||
import google.registry.model.common.FeatureFlag.FeatureStatus;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link FeatureFlag}. */
|
||||
public class FeatureFlagTest extends EntityTestCase {
|
||||
|
||||
public FeatureFlagTest() {
|
||||
super(JpaEntityCoverageCheck.ENABLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_persistence() {
|
||||
FeatureFlag featureFlag =
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("testFlag")
|
||||
.setStatus(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(8), ACTIVE)
|
||||
.build())
|
||||
.build();
|
||||
persistResource(featureFlag);
|
||||
FeatureFlag flagFromDb = loadByEntity(featureFlag);
|
||||
assertThat(featureFlag).isEqualTo(flagFromDb);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_getSingleFlag() {
|
||||
FeatureFlag featureFlag =
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("testFlag")
|
||||
.setStatus(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(8), ACTIVE)
|
||||
.build())
|
||||
.build();
|
||||
persistResource(featureFlag);
|
||||
assertThat(FeatureFlag.get("testFlag")).isEqualTo(featureFlag);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_getMultipleFlags() {
|
||||
FeatureFlag featureFlag1 =
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("testFlag1")
|
||||
.setStatus(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(8), ACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
FeatureFlag featureFlag2 =
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("testFlag2")
|
||||
.setStatus(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(3), INACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
FeatureFlag featureFlag3 =
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("testFlag3")
|
||||
.setStatus(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
ImmutableSet<FeatureFlag> featureFlags =
|
||||
FeatureFlag.get(ImmutableSet.of("testFlag1", "testFlag2", "testFlag3"));
|
||||
assertThat(featureFlags.size()).isEqualTo(3);
|
||||
assertThat(featureFlags).containsExactly(featureFlag1, featureFlag2, featureFlag3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_getMultipleFlagsOneMissing() {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("testFlag1")
|
||||
.setStatus(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(8), ACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("testFlag2")
|
||||
.setStatus(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(3), INACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
FeatureFlagNotFoundException thrown =
|
||||
assertThrows(
|
||||
FeatureFlagNotFoundException.class,
|
||||
() -> FeatureFlag.get(ImmutableSet.of("missingFlag", "testFlag1", "testFlag2")));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("No feature flag object(s) found for missingFlag");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_featureFlagNotPresent() {
|
||||
FeatureFlagNotFoundException thrown =
|
||||
assertThrows(FeatureFlagNotFoundException.class, () -> FeatureFlag.get("fakeFlag"));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("No feature flag object(s) found for fakeFlag");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_resetFeatureName() {
|
||||
FeatureFlag featureFlag =
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("testFlag")
|
||||
.setStatus(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(8), ACTIVE)
|
||||
.build())
|
||||
.build();
|
||||
IllegalStateException thrown =
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() -> featureFlag.asBuilder().setFeatureName("differentName"));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Feature name can only be set once");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_nullFeatureName() {
|
||||
FeatureFlag.Builder featureFlagBuilder =
|
||||
new FeatureFlag.Builder()
|
||||
.setStatus(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(8), ACTIVE)
|
||||
.build());
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(IllegalArgumentException.class, () -> featureFlagBuilder.build());
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Feature name must not be null or empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_emptyFeatureName() {
|
||||
FeatureFlag.Builder featureFlagBuilder =
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("")
|
||||
.setStatus(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(8), ACTIVE)
|
||||
.build());
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(IllegalArgumentException.class, () -> featureFlagBuilder.build());
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Feature name must not be null or empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_invalidStatusMap() {
|
||||
FeatureFlag.Builder featureFlagBuilder = new FeatureFlag.Builder().setFeatureName("testFlag");
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
featureFlagBuilder.setStatus(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(DateTime.now(UTC).plusWeeks(8), ACTIVE)
|
||||
.build()));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Must provide transition entry for the start of time (Unix Epoch)");
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,7 @@ import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationW
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.SerializeUtils;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -69,7 +70,7 @@ public class DomainSqlTest {
|
||||
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
|
||||
|
||||
private Domain domain;
|
||||
private VKey<Contact> contactKey;
|
||||
private Optional<VKey<Contact>> contactKey;
|
||||
private VKey<Contact> contact2Key;
|
||||
private VKey<Host> host1VKey;
|
||||
private Host host;
|
||||
@@ -82,7 +83,7 @@ public class DomainSqlTest {
|
||||
saveRegistrar("registrar1");
|
||||
saveRegistrar("registrar2");
|
||||
saveRegistrar("registrar3");
|
||||
contactKey = createKey(Contact.class, "contact_id1");
|
||||
contactKey = Optional.of(createKey(Contact.class, "contact_id1"));
|
||||
contact2Key = createKey(Contact.class, "contact_id2");
|
||||
|
||||
host1VKey = createKey(Host.class, "host1");
|
||||
|
||||
@@ -185,7 +185,7 @@ public class DomainTest {
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED,
|
||||
StatusValue.SERVER_RENEW_PROHIBITED,
|
||||
StatusValue.SERVER_HOLD))
|
||||
.setRegistrant(contact1Key)
|
||||
.setRegistrant(Optional.of(contact1Key))
|
||||
.setNameservers(ImmutableSet.of(hostKey))
|
||||
.setSubordinateHosts(ImmutableSet.of("ns1.example.com"))
|
||||
.setPersistedCurrentSponsorRegistrarId("NewRegistrar")
|
||||
@@ -242,6 +242,16 @@ public class DomainTest {
|
||||
.hasValue(domain);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegistrantNotRequired() {
|
||||
persistResource(domain.asBuilder().setRegistrant(Optional.empty()).build());
|
||||
assertThat(
|
||||
loadByForeignKey(Domain.class, domain.getForeignKey(), fakeClock.nowUtc())
|
||||
.get()
|
||||
.getRegistrant())
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyStringsBecomeNull() {
|
||||
assertThat(
|
||||
@@ -1012,14 +1022,14 @@ public class DomainTest {
|
||||
DesignatedContact.create(Type.BILLING, contact3Key),
|
||||
DesignatedContact.create(Type.TECH, contact4Key)),
|
||||
true);
|
||||
assertThat(domain.getRegistrant()).isEqualTo(contact1Key);
|
||||
assertThat(domain.getRegistrant()).hasValue(contact1Key);
|
||||
assertThat(domain.getAdminContact()).isEqualTo(contact2Key);
|
||||
assertThat(domain.getBillingContact()).isEqualTo(contact3Key);
|
||||
assertThat(domain.getTechContact()).isEqualTo(contact4Key);
|
||||
|
||||
// Make sure everything gets nulled out.
|
||||
domain.setContactFields(ImmutableSet.of(), true);
|
||||
assertThat(domain.getRegistrant()).isNull();
|
||||
assertThat(domain.getRegistrant()).isEmpty();
|
||||
assertThat(domain.getAdminContact()).isNull();
|
||||
assertThat(domain.getBillingContact()).isNull();
|
||||
assertThat(domain.getTechContact()).isNull();
|
||||
@@ -1032,13 +1042,13 @@ public class DomainTest {
|
||||
DesignatedContact.create(Type.BILLING, contact3Key),
|
||||
DesignatedContact.create(Type.TECH, contact4Key)),
|
||||
false);
|
||||
assertThat(domain.getRegistrant()).isNull();
|
||||
assertThat(domain.getRegistrant()).isEmpty();
|
||||
assertThat(domain.getAdminContact()).isEqualTo(contact2Key);
|
||||
assertThat(domain.getBillingContact()).isEqualTo(contact3Key);
|
||||
assertThat(domain.getTechContact()).isEqualTo(contact4Key);
|
||||
domain = domain.asBuilder().setRegistrant(contact1Key).build();
|
||||
domain = domain.asBuilder().setRegistrant(Optional.of(contact1Key)).build();
|
||||
domain.setContactFields(ImmutableSet.of(), false);
|
||||
assertThat(domain.getRegistrant()).isEqualTo(contact1Key);
|
||||
assertThat(domain.getRegistrant()).hasValue(contact1Key);
|
||||
assertThat(domain.getAdminContact()).isNull();
|
||||
assertThat(domain.getBillingContact()).isNull();
|
||||
assertThat(domain.getTechContact()).isNull();
|
||||
|
||||
@@ -31,6 +31,7 @@ import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.transfer.ContactTransferData;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -68,7 +69,7 @@ public final class Spec11ThreatMatchTest extends EntityTestCase {
|
||||
.setDomainName("foo.tld")
|
||||
.setRepoId(domainRepoId)
|
||||
.setNameservers(hostVKey)
|
||||
.setRegistrant(registrantContactVKey)
|
||||
.setRegistrant(Optional.of(registrantContactVKey))
|
||||
.setContacts(ImmutableSet.of())
|
||||
.build();
|
||||
|
||||
|
||||
@@ -325,6 +325,16 @@ public final class TldTest extends EntityTestCase {
|
||||
.values());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_testGetAllMissingTld() {
|
||||
createTld("foo");
|
||||
TldNotFoundException thrown =
|
||||
assertThrows(
|
||||
TldNotFoundException.class,
|
||||
() -> assertThat(Tld.get(ImmutableSet.of("foo", "tld", "missing"))));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("No TLD object(s) found for missing");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetReservedLists() {
|
||||
ReservedList rl5 =
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.rdap;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResources;
|
||||
@@ -278,6 +279,19 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_contacts_with_remark.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidDomain_notLoggedIn_contactsShowRedacted_evenWhenRegistrantDoesntExist() {
|
||||
// Even though the registrant is empty on this domain, it still shows a full set of REDACTED
|
||||
// fields through RDAP.
|
||||
persistResource(
|
||||
loadByForeignKey(Domain.class, "cat.lol", clock.nowUtc())
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setRegistrant(Optional.empty())
|
||||
.build());
|
||||
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_contacts_with_remark.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidDomain_loggedInAsOtherRegistrar_noContacts() {
|
||||
login("idnregistrar");
|
||||
|
||||
@@ -52,6 +52,8 @@ import google.registry.rdap.RdapObjectClasses.ReplyPayloadBase;
|
||||
import google.registry.rdap.RdapObjectClasses.TopLevelReplyObject;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FullFieldsTestEntityHelper;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -77,7 +79,7 @@ class RdapJsonFormatterTest {
|
||||
private Host hostNoAddresses;
|
||||
private Host hostNotLinked;
|
||||
private Host hostSuperordinatePendingTransfer;
|
||||
private Contact contactRegistrant;
|
||||
@Nullable private Contact contactRegistrant;
|
||||
private Contact contactAdmin;
|
||||
private Contact contactTech;
|
||||
private Contact contactNotLinked;
|
||||
@@ -371,7 +373,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactRegistrant,
|
||||
Optional.of(contactRegistrant),
|
||||
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
@@ -384,7 +386,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactRegistrant,
|
||||
Optional.of(contactRegistrant),
|
||||
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
|
||||
OutputDataType.SUMMARY)
|
||||
.toJson())
|
||||
@@ -398,7 +400,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactRegistrant,
|
||||
Optional.of(contactRegistrant),
|
||||
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
@@ -418,7 +420,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactRegistrant,
|
||||
Optional.of(contactRegistrant),
|
||||
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
@@ -431,7 +433,9 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactAdmin, ImmutableSet.of(RdapEntity.Role.ADMIN), OutputDataType.FULL)
|
||||
Optional.of(contactAdmin),
|
||||
ImmutableSet.of(RdapEntity.Role.ADMIN),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
.isEqualTo(loadJson("rdapjson_admincontact.json"));
|
||||
}
|
||||
@@ -442,7 +446,9 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactTech, ImmutableSet.of(RdapEntity.Role.TECH), OutputDataType.FULL)
|
||||
Optional.of(contactTech),
|
||||
ImmutableSet.of(RdapEntity.Role.TECH),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
.isEqualTo(loadJson("rdapjson_techcontact.json"));
|
||||
}
|
||||
@@ -452,7 +458,8 @@ class RdapJsonFormatterTest {
|
||||
assertAboutJson()
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(contactTech, ImmutableSet.of(), OutputDataType.FULL)
|
||||
.createRdapContactEntity(
|
||||
Optional.of(contactTech), ImmutableSet.of(), OutputDataType.FULL)
|
||||
.toJson())
|
||||
.isEqualTo(loadJson("rdapjson_rolelesscontact.json"));
|
||||
}
|
||||
@@ -462,7 +469,8 @@ class RdapJsonFormatterTest {
|
||||
assertAboutJson()
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(contactNotLinked, ImmutableSet.of(), OutputDataType.FULL)
|
||||
.createRdapContactEntity(
|
||||
Optional.of(contactNotLinked), ImmutableSet.of(), OutputDataType.FULL)
|
||||
.toJson())
|
||||
.isEqualTo(loadJson("rdapjson_unlinkedcontact.json"));
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ import google.registry.xjc.rdedomain.XjcRdeDomainElement;
|
||||
import google.registry.xjc.rgp.XjcRgpStatusType;
|
||||
import google.registry.xjc.secdns.XjcSecdnsDsDataType;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -280,9 +281,14 @@ public class DomainToXjcConverterTest {
|
||||
makeHost(clock, "4-Q9JYB4C", "ns2.cat.みんな", "bad:f00d:cafe::15:beef")
|
||||
.createVKey()))
|
||||
.setRegistrant(
|
||||
makeContact(
|
||||
clock, "12-Q9JYB4C", "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな")
|
||||
.createVKey())
|
||||
Optional.of(
|
||||
makeContact(
|
||||
clock,
|
||||
"12-Q9JYB4C",
|
||||
"5372808-ERL",
|
||||
"(◕‿◕) nevermore",
|
||||
"prophet@evil.みんな")
|
||||
.createVKey()))
|
||||
.setRegistrationExpirationTime(DateTime.parse("1930-01-01T00:00:00Z"))
|
||||
.setGracePeriods(
|
||||
ImmutableSet.of(
|
||||
|
||||
@@ -51,6 +51,7 @@ import google.registry.model.transfer.DomainTransferData;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.Idn;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -63,8 +64,9 @@ final class RdeFixtures {
|
||||
.setDomainName("example." + tld)
|
||||
.setRepoId(generateNewDomainRoid(tld))
|
||||
.setRegistrant(
|
||||
makeContact(clock, "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな")
|
||||
.createVKey())
|
||||
Optional.of(
|
||||
makeContact(clock, "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな")
|
||||
.createVKey()))
|
||||
.build();
|
||||
DomainHistory historyEntry =
|
||||
persistResource(
|
||||
|
||||
@@ -23,6 +23,7 @@ import google.registry.bsa.persistence.BsaUnblockableDomainTest;
|
||||
import google.registry.model.billing.BillingBaseTest;
|
||||
import google.registry.model.common.CursorTest;
|
||||
import google.registry.model.common.DnsRefreshRequestTest;
|
||||
import google.registry.model.common.FeatureFlagTest;
|
||||
import google.registry.model.console.ConsoleEppActionHistoryTest;
|
||||
import google.registry.model.console.RegistrarPocUpdateHistoryTest;
|
||||
import google.registry.model.console.RegistrarUpdateHistoryTest;
|
||||
@@ -103,6 +104,7 @@ import org.junit.runner.RunWith;
|
||||
DnsRefreshRequestTest.class,
|
||||
DomainSqlTest.class,
|
||||
DomainHistoryTest.class,
|
||||
FeatureFlagTest.class,
|
||||
HostHistoryTest.class,
|
||||
LockTest.class,
|
||||
PollMessageTest.class,
|
||||
|
||||
@@ -190,7 +190,7 @@ public final class DatabaseHelper {
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setCreationTimeForTest(START_OF_TIME)
|
||||
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("2fooBAR")))
|
||||
.setRegistrant(contactKey)
|
||||
.setRegistrant(Optional.of(contactKey))
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, contactKey),
|
||||
@@ -603,7 +603,7 @@ public final class DatabaseHelper {
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setCreationTimeForTest(creationTime)
|
||||
.setRegistrationExpirationTime(expirationTime)
|
||||
.setRegistrant(contact.createVKey())
|
||||
.setRegistrant(Optional.of(contact.createVKey()))
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, contact.createVKey()),
|
||||
|
||||
@@ -46,6 +46,7 @@ import google.registry.persistence.VKey;
|
||||
import google.registry.util.Idn;
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -351,7 +352,7 @@ public final class FullFieldsTestEntityHelper {
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED))
|
||||
.setDsData(ImmutableSet.of(DomainDsData.create(1, 2, 3, "deadface")));
|
||||
if (registrant != null) {
|
||||
builder.setRegistrant(registrant.createVKey());
|
||||
builder.setRegistrant(Optional.of(registrant.createVKey()));
|
||||
}
|
||||
if ((admin != null) || (tech != null)) {
|
||||
ImmutableSet.Builder<DesignatedContact> contactsBuilder = new ImmutableSet.Builder<>();
|
||||
|
||||
@@ -41,6 +41,7 @@ import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.whois.WhoisResponse.WhoisResponseResults;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -264,7 +265,7 @@ class DomainWhoisResponseTest {
|
||||
StatusValue.CLIENT_RENEW_PROHIBITED,
|
||||
StatusValue.CLIENT_TRANSFER_PROHIBITED,
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED))
|
||||
.setRegistrant(registrantResourceKey)
|
||||
.setRegistrant(Optional.of(registrantResourceKey))
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(DesignatedContact.Type.ADMIN, adminResourceKey),
|
||||
@@ -291,6 +292,21 @@ class DomainWhoisResponseTest {
|
||||
.isEqualTo(WhoisResponseResults.create(loadFile("whois_domain.txt"), 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPlainTextOutputTest_noRegistrant() {
|
||||
DomainWhoisResponse domainWhoisResponse =
|
||||
new DomainWhoisResponse(
|
||||
domain.asBuilder().setRegistrant(Optional.empty()).build(),
|
||||
false,
|
||||
"Please contact registrar",
|
||||
clock.nowUtc());
|
||||
assertThat(
|
||||
domainWhoisResponse.getResponse(
|
||||
false,
|
||||
"Doodle Disclaimer\nI exist so that carriage return\nin disclaimer can be tested."))
|
||||
.isEqualTo(WhoisResponseResults.create(loadFile("whois_domain_no_registrant.txt"), 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPlainTextOutputTest_registrarAbuseInfoMissing() {
|
||||
persistResource(abuseContact.asBuilder().setVisibleInDomainWhoisAsAbuse(false).build());
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<response>
|
||||
<result code="1000">
|
||||
<msg>Command completed successfully</msg>
|
||||
</result>
|
||||
<resData>
|
||||
<domain:infData
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:roid>%ROID%</domain:roid>
|
||||
<domain:status s="ok"/>
|
||||
<domain:contact type="admin">sh8013</domain:contact>
|
||||
<domain:contact type="tech">sh8013</domain:contact>
|
||||
<domain:ns>
|
||||
<domain:hostObj>ns1.example.tld</domain:hostObj>
|
||||
<domain:hostObj>ns1.example.net</domain:hostObj>
|
||||
</domain:ns>
|
||||
<domain:host>ns1.example.tld</domain:host>
|
||||
<domain:host>ns2.example.tld</domain:host>
|
||||
<domain:clID>NewRegistrar</domain:clID>
|
||||
<domain:crID>TheRegistrar</domain:crID>
|
||||
<domain:crDate>1999-04-03T22:00:00.0Z</domain:crDate>
|
||||
<domain:upID>NewRegistrar</domain:upID>
|
||||
<domain:upDate>1999-12-03T09:00:00.0Z</domain:upDate>
|
||||
<domain:exDate>2005-04-03T22:00:00.0Z</domain:exDate>
|
||||
<domain:trDate>2000-04-08T09:00:00.0Z</domain:trDate>
|
||||
<domain:authInfo>
|
||||
<domain:pw>2fooBAR</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:infData>
|
||||
</resData>
|
||||
<trID>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
<svTRID>server-trid</svTRID>
|
||||
</trID>
|
||||
</response>
|
||||
</epp>
|
||||
@@ -0,0 +1,53 @@
|
||||
Domain Name: example.tld
|
||||
Registry Domain ID: 3-TLD
|
||||
Registrar WHOIS Server: whois.nic.fakewhois.example
|
||||
Registrar URL: http://my.fake.url
|
||||
Updated Date: 2009-05-29T20:13:00Z
|
||||
Creation Date: 2000-10-08T00:45:00Z
|
||||
Registry Expiry Date: 2010-10-08T00:44:59Z
|
||||
Registrar: New Registrar
|
||||
Registrar IANA ID: 5555555
|
||||
Registrar Abuse Contact Email: jakedoe@theregistrar.com
|
||||
Registrar Abuse Contact Phone: +1.2125551216
|
||||
Domain Status: addPeriod https://icann.org/epp#addPeriod
|
||||
Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited
|
||||
Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited
|
||||
Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited
|
||||
Domain Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited
|
||||
Domain Status: transferPeriod https://icann.org/epp#transferPeriod
|
||||
Registry Admin ID: REDACTED FOR PRIVACY
|
||||
Admin Name: REDACTED FOR PRIVACY
|
||||
Admin Organization: REDACTED FOR PRIVACY
|
||||
Admin Street: REDACTED FOR PRIVACY
|
||||
Admin City: REDACTED FOR PRIVACY
|
||||
Admin State/Province: REDACTED FOR PRIVACY
|
||||
Admin Postal Code: REDACTED FOR PRIVACY
|
||||
Admin Country: REDACTED FOR PRIVACY
|
||||
Admin Phone: REDACTED FOR PRIVACY
|
||||
Admin Phone Ext: REDACTED FOR PRIVACY
|
||||
Admin Fax: REDACTED FOR PRIVACY
|
||||
Admin Email: Please contact registrar
|
||||
Registry Tech ID: REDACTED FOR PRIVACY
|
||||
Tech Name: REDACTED FOR PRIVACY
|
||||
Tech Organization: REDACTED FOR PRIVACY
|
||||
Tech Street: REDACTED FOR PRIVACY
|
||||
Tech City: REDACTED FOR PRIVACY
|
||||
Tech State/Province: REDACTED FOR PRIVACY
|
||||
Tech Postal Code: REDACTED FOR PRIVACY
|
||||
Tech Country: REDACTED FOR PRIVACY
|
||||
Tech Phone: REDACTED FOR PRIVACY
|
||||
Tech Phone Ext: REDACTED FOR PRIVACY
|
||||
Tech Fax: REDACTED FOR PRIVACY
|
||||
Tech Fax Ext: REDACTED FOR PRIVACY
|
||||
Tech Email: Please contact registrar
|
||||
Name Server: ns01.exampleregistrar.tld
|
||||
Name Server: ns02.exampleregistrar.tld
|
||||
DNSSEC: signedDelegation
|
||||
URL of the ICANN Whois Inaccuracy Complaint Form: https://www.icann.org/wicf/
|
||||
>>> Last update of WHOIS database: 2009-05-29T20:15:00Z <<<
|
||||
|
||||
For more information on Whois status codes, please visit https://icann.org/epp
|
||||
|
||||
Doodle Disclaimer
|
||||
I exist so that carriage return
|
||||
in disclaimer can be tested.
|
||||
@@ -463,6 +463,12 @@
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table "FeatureFlag" (
|
||||
feature_name text not null,
|
||||
status hstore not null,
|
||||
primary key (feature_name)
|
||||
);
|
||||
|
||||
create table "GracePeriod" (
|
||||
grace_period_id int8 not null,
|
||||
billing_event_id int8,
|
||||
|
||||
+8
-7
@@ -56,13 +56,14 @@ ext {
|
||||
'org.hibernate:hibernate-core:[5.6.14.Final, 6.0)',
|
||||
'org.hibernate:hibernate-hikaricp:[5.6.14.Final, 6.0)',
|
||||
|
||||
// Netty 5.0 is in alpha and breaks API.
|
||||
'io.netty:netty-codec-http:[4.1.59.Final, 5.0.0)!!',
|
||||
'io.netty:netty-codec:[4.1.59.Final, 5.0.0)!!',
|
||||
'io.netty:netty-common:[4.1.59.Final, 5.0.0)!!',
|
||||
'io.netty:netty-handler:[4.1.59.Final, 5.0.0)!!',
|
||||
'io.netty:netty-transport:[4.1.59.Final, 5.0.0)!!',
|
||||
'io.netty:netty-buffer:[4.1.59.Final, 5.0.0)!!',
|
||||
// Netty 4.2 is in alpha and causes runtime error. Also note that v5.0
|
||||
// seems abandoned (last updated on Maven in 2015).
|
||||
'io.netty:netty-codec-http:[4.1.59.Final, 4.2.0)!!',
|
||||
'io.netty:netty-codec:[4.1.59.Final, 4.2.0)!!',
|
||||
'io.netty:netty-common:[4.1.59.Final, 4.2.0)!!',
|
||||
'io.netty:netty-handler:[4.1.59.Final, 4.2.0)!!',
|
||||
'io.netty:netty-transport:[4.1.59.Final, 4.2.0)!!',
|
||||
'io.netty:netty-buffer:[4.1.59.Final, 4.2.0)!!',
|
||||
|
||||
// OkHttp 5.0 is in alpha.
|
||||
'com.squareup.okhttp3:okhttp:[4.10.0, 5.0.0)!!',
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
Reference in New Issue
Block a user