1
0
mirror of https://github.com/google/nomulus synced 2025-12-23 06:15:42 +00:00

Add environment support to the console build (#2539)

This commit is contained in:
Pavlo Tkach
2024-08-30 14:31:28 -04:00
committed by GitHub
parent 1765f4f0b4
commit ab5f6cc229
22 changed files with 164 additions and 83 deletions

View File

@@ -13,7 +13,7 @@ Webapp is deployed with the nomulus default service war to Google App Engine.
During nomulus default service war build task, gradle script triggers the
following:
1) Console webapp build script `buildConsoleWebappProd`, which installs
1) Console webapp build script `buildConsoleWebapp`, which installs
dependencies, assembles a compiled ts -> js, minified, optimized static
artifact (html, css, js)
2) Artifact assembled in step 1 then gets copied to core project web artifact

View File

@@ -63,6 +63,39 @@
],
"outputHashing": "all"
},
"sandbox": {
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.sandbox.ts"
}
],
"outputHashing": "all"
},
"crash": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
},
"alpha": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
},
"development": {
"optimization": false,
"extractLicenses": false,
@@ -78,6 +111,15 @@
"production": {
"buildTarget": "console-webapp:build:production"
},
"alpha": {
"buildTarget": "console-webapp:build:alpha"
},
"crash": {
"buildTarget": "console-webapp:build:crash"
},
"sandbox": {
"buildTarget": "console-webapp:build:sandbox"
},
"development": {
"buildTarget": "console-webapp:build:development"
}

View File

@@ -37,17 +37,16 @@ task runConsoleWebappUnitTests(type: Exec) {
args 'run', 'test'
}
task buildConsoleWebappNonProd(type: Exec) {
task buildConsoleWebapp(type: Exec) {
workingDir "${consoleDir}/"
executable 'npm'
args 'run', 'build'
}
// Keeping the same as non prod for now before we figure out optimization we want to include
task buildConsoleWebappProd(type: Exec) {
workingDir "${consoleDir}/"
executable 'npm'
args 'run', 'build'
def configuration = project.hasProperty('configuration') ?
project.getProperty('configuration') :
'production'
args 'run', "build", "--configuration=${configuration}"
doFirst {
println "Building console for environment: ${configuration}"
}
}
task applyFormatting(type: Exec) {
@@ -68,10 +67,10 @@ task deploy(type: Exec) {
args 'app', 'deploy', "${projectParam}", '--quiet'
}
tasks.buildConsoleWebappProd.dependsOn(tasks.npmInstallDeps)
tasks.buildConsoleWebapp.dependsOn(tasks.npmInstallDeps)
tasks.runConsoleWebappUnitTests.dependsOn(tasks.npmInstallDeps)
tasks.applyFormatting.dependsOn(tasks.npmInstallDeps)
tasks.checkFormatting.dependsOn(tasks.npmInstallDeps)
tasks.build.dependsOn(tasks.checkFormatting)
tasks.build.dependsOn(tasks.runConsoleWebappUnitTests)
tasks.deploy.dependsOn(tasks.buildConsoleWebappProd)
tasks.deploy.dependsOn(tasks.buildConsoleWebapp)

View File

@@ -4,9 +4,9 @@
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config dev-proxy.config.json",
"build": "ng build --base-href=/console/",
"build": "ng build --base-href=/console/ --configuration=$npm_config_configuration",
"build:local": "ng build --base-href=/default/console/",
"watch": "ng build --watch --configuration development",
"watch": "ng build --watch --configuration=development",
"test": "ng test --browsers=ChromeHeadless --watch=false",
"run:dev": "",
"prettify": "npx prettier --write ./src/",
@@ -54,4 +54,4 @@
"prettier": "2.8.7",
"typescript": "~5.4.5"
}
}
}

View File

@@ -34,6 +34,10 @@ export interface RouteWithIcon extends Route {
iconName?: string;
}
export const PATHS = {
NewOteComponent: 'new-ote',
OteStatusComponent: 'ote-status/:registrarId',
};
export const routes: RouteWithIcon[] = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{
@@ -41,12 +45,14 @@ export const routes: RouteWithIcon[] = [
component: RegistryLockVerifyComponent,
},
{
path: NewOteComponent.PATH,
component: NewOteComponent,
path: PATHS.NewOteComponent,
loadComponent: () =>
import('./ote/newOte.component').then((mod) => mod.NewOteComponent),
},
{
path: OteStatusComponent.PATH,
component: OteStatusComponent,
path: PATHS.OteStatusComponent,
loadComponent: () =>
import('./ote/oteStatus.component').then((mod) => mod.OteStatusComponent),
},
{ path: 'registrars', component: RegistrarComponent },
{

View File

@@ -16,16 +16,16 @@ 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 { MaterialModule } from './material.module';
import { BackendService } from './shared/services/backend.service';
import { AppRoutingModule } from './app-routing.module';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [AppComponent],
imports: [RouterTestingModule, MaterialModule, BrowserAnimationsModule],
imports: [MaterialModule, BrowserAnimationsModule, AppRoutingModule],
providers: [
BackendService,
provideHttpClient(),

View File

@@ -68,8 +68,6 @@ import { TldsComponent } from './tlds/tlds.component';
HeaderComponent,
HomeComponent,
LocationBackDirective,
NewOteComponent,
OteStatusComponent,
UserLevelVisibility,
NavigationComponent,
NewRegistrarComponent,
@@ -99,6 +97,7 @@ import { TldsComponent } from './tlds/tlds.component';
MaterialModule,
SnackBarModule,
],
exports: [SelectedRegistrarWrapper],
providers: [
BackendService,
BreakPointObserverService,

View File

@@ -4,4 +4,8 @@
white-space: break-spaces;
padding: 20px;
}
mat-form-field {
width: 100%;
max-width: 350px;
}
}

View File

@@ -17,6 +17,8 @@ import { Component, computed, signal } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { RegistrarService } from '../registrar/registrar.service';
import { MaterialModule } from '../material.module';
import { SnackBarModule } from '../snackbar.module';
export interface OteCreateResponse extends Map<string, string> {
password: string;
@@ -24,12 +26,12 @@ export interface OteCreateResponse extends Map<string, string> {
@Component({
selector: 'app-ote',
standalone: true,
imports: [MaterialModule, SnackBarModule],
templateUrl: './newOte.component.html',
styleUrls: ['./newOte.component.scss'],
})
export class NewOteComponent {
public static PATH = 'new-ote';
oteCreateResponse = signal<OteCreateResponse | undefined>(undefined);
readonly oteCreateResponseFormatted = computed(() => {

View File

@@ -1,31 +1,28 @@
<app-selected-registrar-wrapper>
<h1 class="mat-headline-4">OT&E Status Check</h1>
@if(isOte()) {
<h1 *ngIf="oteStatusResponse().length">
Status:
<span>{{ oteStatusUnfinished().length ? "Unfinished" : "Completed" }}</span>
</h1>
<div class="console-app__ote-status">
@if(oteStatusCompleted().length) {
<div class="console-app__ote-status_completed">
<h1>Completed</h1>
<div *ngFor="let entry of oteStatusCompleted()">
<mat-icon>check_box</mat-icon>{{ entry.description }}
</div>
<h1 class="mat-headline-4">OT&E Status Check</h1>
@if(registrarId() === null) {
<h1>Missing registrarId param</h1>
} @else if(isOte()) {
<h1 *ngIf="oteStatusResponse().length">
Status:
<span>{{ oteStatusUnfinished().length ? "Unfinished" : "Completed" }}</span>
</h1>
<div class="console-app__ote-status">
@if(oteStatusCompleted().length) {
<div class="console-app__ote-status_completed">
<h1>Completed</h1>
<div *ngFor="let entry of oteStatusCompleted()">
<mat-icon>check_box</mat-icon>{{ entry.description }}
</div>
</div>
} @if(oteStatusUnfinished().length) {
<div class="console-app__ote-status_unfinished">
<h1>Unfinished</h1>
<div *ngFor="let entry of oteStatusUnfinished()">
<mat-icon>check_box_outline_blank</mat-icon>{{ entry.description }}
</div>
} @if(oteStatusUnfinished().length) {
<div class="console-app__ote-status_unfinished">
<h1>Unfinished</h1>
<div *ngFor="let entry of oteStatusUnfinished()">
<mat-icon>check_box_outline_blank</mat-icon>{{ entry.description }}
</div>
</div>
}
</div>
} @else {
<h1>
Registrar {{ registrarService.registrar()?.registrarId }} is not an OT&E
registrar
</h1>
}
</app-selected-registrar-wrapper>
</div>
} @else {
<h1>Registrar {{ registrarId() }} is not an OT&E registrar</h1>
}

View File

@@ -13,9 +13,14 @@
// limitations under the License.
import { HttpErrorResponse } from '@angular/common/http';
import { Component, computed, signal } from '@angular/core';
import { Component, computed, OnInit, signal } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { RegistrarService } from '../registrar/registrar.service';
import { MaterialModule } from '../material.module';
import { SnackBarModule } from '../snackbar.module';
import { CommonModule } from '@angular/common';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { take } from 'rxjs';
export interface OteStatusResponse {
description: string;
@@ -26,12 +31,13 @@ export interface OteStatusResponse {
@Component({
selector: 'app-ote-status',
standalone: true,
imports: [MaterialModule, SnackBarModule, CommonModule],
templateUrl: './oteStatus.component.html',
styleUrls: ['./oteStatus.component.scss'],
})
export class OteStatusComponent {
public static PATH = 'ote-status';
export class OteStatusComponent implements OnInit {
registrarId = signal<string | null>(null);
oteStatusResponse = signal<OteStatusResponse[]>([]);
oteStatusCompleted = computed(() =>
@@ -41,16 +47,26 @@ export class OteStatusComponent {
this.oteStatusResponse().filter((v) => !v.completed)
);
isOte = computed(
() => this.registrarService.registrar()?.type?.toLowerCase() === 'ote'
() =>
this.registrarService
.registrars()
.find((r) => r.registrarId === this.registrarId())
?.type?.toLowerCase() === 'ote'
);
constructor(
private route: ActivatedRoute,
protected registrarService: RegistrarService,
private _snackBar: MatSnackBar
) {
this.registrarService
.oteStatus(this.registrarService.registrarId())
.subscribe({
) {}
ngOnInit(): void {
this.route.paramMap.pipe(take(1)).subscribe((params: ParamMap) => {
this.registrarId.set(params.get('registrarId'));
const registrarId = this.registrarId();
if (!registrarId) throw 'Missing registrarId param';
this.registrarService.oteStatus(registrarId).subscribe({
next: (oteStatusResponse: OteStatusResponse[]) => {
this.oteStatusResponse.set(oteStatusResponse);
},
@@ -58,5 +74,6 @@ export class OteStatusComponent {
this._snackBar.open(err.error || err.message);
},
});
});
}
}

View File

@@ -9,6 +9,7 @@
<div class="spacer"></div>
@if(!inEdit && !registrarNotFound) {
<button
*ngIf="oteButtonVisible"
mat-stroked-button
(click)="checkOteStatus()"
aria-label="Check OT&E account"

View File

@@ -18,10 +18,10 @@ import { MatChipInputEvent } from '@angular/material/chips';
import { MatSnackBar } from '@angular/material/snack-bar';
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { OteStatusComponent } from '../ote/oteStatus.component';
import { RESTRICTED_ELEMENTS } from '../shared/directives/userLevelVisiblity.directive';
import { Registrar, RegistrarService } from './registrar.service';
import { RegistrarComponent, columns } from './registrarsTable.component';
import { environment } from '../../environments/environment';
@Component({
selector: 'app-registrar-details',
@@ -31,8 +31,9 @@ import { RegistrarComponent, columns } from './registrarsTable.component';
export class RegistrarDetailsComponent implements OnInit {
public static PATH = 'registrars/:id';
inEdit: boolean = false;
oteButtonVisible = environment.sandbox;
registrarInEdit!: Registrar;
registrarNotFound: boolean = false;
registrarNotFound: boolean = true;
columns = columns.filter((c) => !c.hiddenOnDetailsCard);
private subscription!: Subscription;
@@ -73,7 +74,9 @@ export class RegistrarDetailsComponent implements OnInit {
}
checkOteStatus() {
this.router.navigate([OteStatusComponent.PATH]);
this.router.navigate(['ote-status/', this.registrarInEdit.registrarId], {
queryParamsHandling: 'merge',
});
}
getElementIdForOteBlock() {

View File

@@ -7,6 +7,7 @@
<div class="spacer"></div>
<button
mat-stroked-button
*ngIf="oteButtonVisible"
(click)="createOteAccount()"
aria-label="Generate OT&E accounts"
[elementId]="getElementIdForOteBlock()"

View File

@@ -17,9 +17,10 @@ import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { Router } from '@angular/router';
import { NewOteComponent } from '../ote/newOte.component';
import { RESTRICTED_ELEMENTS } from '../shared/directives/userLevelVisiblity.directive';
import { Registrar, RegistrarService } from './registrar.service';
import { PATHS } from '../app-routing.module';
import { environment } from '../../environments/environment';
export const columns = [
{
@@ -82,7 +83,7 @@ export class RegistrarComponent {
public static PATH = 'registrars';
dataSource: MatTableDataSource<Registrar>;
columns = columns;
oteButtonVisible = environment.sandbox;
displayedColumns = this.columns.map((c) => c.columnDef);
@ViewChild(MatPaginator) paginator!: MatPaginator;
@@ -106,7 +107,7 @@ export class RegistrarComponent {
}
createOteAccount() {
this.router.navigate([NewOteComponent.PATH]);
this.router.navigate([PATHS.NewOteComponent]);
}
getElementIdForOteBlock() {

View File

@@ -14,4 +14,5 @@
export const environment = {
production: true,
sandbox: false,
};

View File

@@ -0,0 +1,18 @@
// 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.
export const environment = {
production: false,
sandbox: true,
};

View File

@@ -18,6 +18,7 @@
export const environment = {
production: false,
sandbox: false,
};
/*

View File

@@ -18,7 +18,7 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
if (environment.production || environment.sandbox) {
enableProdMode();
}