Refactor config-new component and add form validation
Refactors the config-new component form to add input constraints and validation. Change-Id: I930e6bf50def29d894c8ecc9cd5db9e3ed13d0cf
This commit is contained in:
parent
61b9b19be6
commit
d0ccf65d33
@ -62,8 +62,10 @@ export class ConfigManifestComponent implements OnInit {
|
||||
});
|
||||
|
||||
const checkout = this.getCheckoutRef(repo);
|
||||
repoGroup.controls.checkoutLabel.setValue(checkout[0]);
|
||||
repoGroup.controls.checkoutReference.setValue(checkout[1]);
|
||||
if (checkout !== null) {
|
||||
repoGroup.controls.checkoutLabel.setValue(checkout[0]);
|
||||
repoGroup.controls.checkoutReference.setValue(checkout[1]);
|
||||
}
|
||||
repoArray.push(repoGroup);
|
||||
this.selectArray.push(name);
|
||||
}
|
||||
|
@ -19,3 +19,7 @@
|
||||
.text-input {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
mat-form-field {
|
||||
width: 80%;
|
||||
}
|
||||
|
@ -1,18 +1,171 @@
|
||||
<h1 mat-dialog-title>New {{data.formName}} configuration</h1>
|
||||
<div mat-dialog-content class="form-content">
|
||||
<form [formGroup]="group">
|
||||
<div *ngFor="let key of keys">
|
||||
<mat-form-field *ngIf="!isBool(dataObj[key])" appearance="fill">
|
||||
<mat-label>{{key}}</mat-label>
|
||||
<input class="text-input" formControlName="{{key}}" matInput>
|
||||
<div mat-dialog-content class="form-content" [ngSwitch]="data.formName">
|
||||
<div *ngSwitchCase="'management'" [formGroup]="group">
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput formControlName="Name">
|
||||
<mat-error *ngIf="group.controls.Name.hasError('required')">
|
||||
Name is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Type</mat-label>
|
||||
<input matInput formControlName="type">
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>System Action Retries</mat-label>
|
||||
<input matInput formControlName="systemActionRetries">
|
||||
<mat-error *ngIf="group.controls.systemActionRetries.hasError('pattern')">
|
||||
Value must be a number
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>System Reboot Delay</mat-label>
|
||||
<input matInput formControlName="systemRebootDelay">
|
||||
<mat-error *ngIf="group.controls.systemRebootDelay.hasError('pattern')">
|
||||
Value must be a number
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<p>
|
||||
<mat-checkbox formControlName="useproxy" labelPosition="after">Use Proxy</mat-checkbox>
|
||||
</p>
|
||||
<p>
|
||||
<mat-checkbox formControlName="insecure" labelPosition="after">Insecure</mat-checkbox>
|
||||
</p>
|
||||
</div>
|
||||
<div *ngSwitchCase="'context'" [formGroup]="group">
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Name</mat-label>
|
||||
<input formControlName="Name" matInput>
|
||||
<mat-error *ngIf="group.controls.Name.hasError('required')">
|
||||
Name is required
|
||||
</mat-error>
|
||||
</mat-form-field><br />
|
||||
<mat-form-field>
|
||||
<mat-label>Manifest</mat-label>
|
||||
<mat-select formControlName="Manifest">
|
||||
<mat-option *ngFor="let m of data.configs['manifests']" [value]="m">{{m}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field><br />
|
||||
<mat-form-field>
|
||||
<mat-label>Encryption Config</mat-label>
|
||||
<mat-select formControlName="EncryptionConfig">
|
||||
<!-- Encryption config isn't required, so allow a null option -->
|
||||
<mat-option [value]="null">None</mat-option>
|
||||
<mat-option *ngFor="let e of data.configs['encryption']" [value]="e">{{e}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field><br />
|
||||
<mat-form-field>
|
||||
<mat-label>Management Config</mat-label>
|
||||
<mat-select formControlName="ManagementConfiguration">
|
||||
<mat-option *ngFor="let m of data.configs['management']" [value]="m">{{m}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div *ngSwitchCase="'encryption'" [formGroup]="group">
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput formControlName="Name">
|
||||
<mat-error *ngIf="group.controls.Name.hasError('required')">
|
||||
Name is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>Config Type</mat-label>
|
||||
<mat-select [(value)]="encryptionType" (selectionChange)="onEncryptionChange($event)">
|
||||
<mat-option value="encryption">Encrypt / Decrypt Key</mat-option>
|
||||
<mat-option value="secret">Secret</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<div *ngIf="encryptionType === 'encryption'">
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>EncryptionKeyPath</mat-label>
|
||||
<input matInput formControlName="EncryptionKeyPath">
|
||||
<mat-error *ngIf="group.controls.EncryptionKeyPath.hasError('required')">
|
||||
EncryptionKeyPath is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>DecryptionKeyPath</mat-label>
|
||||
<input matInput formControlName="DecryptionKeyPath">
|
||||
<mat-error *ngIf="group.controls.DecryptionKeyPath.hasError('required')">
|
||||
DecryptionKeyPath is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<p *ngIf="isBool(dataObj[key])">
|
||||
<mat-checkbox formControlName="{{key}}" labelPosition="before">{{key}} </mat-checkbox>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
<div *ngIf="encryptionType === 'secret'">
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>KeySecretName</mat-label>
|
||||
<input matInput formControlName="KeySecretName">
|
||||
<mat-error *ngIf="group.controls.KeySecretName.hasError('required')">
|
||||
KeySecretName is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>KeySecretNamespace</mat-label>
|
||||
<input matInput formControlName="KeySecretNamespace">
|
||||
<mat-error *ngIf="group.controls.KeySecretNamespace.hasError('required')">
|
||||
KeySecretNamespace is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngSwitchCase="'manifest'" [formGroup]="group">
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput formControlName="Name">
|
||||
<mat-error *ngIf="group.controls.Name.hasError('required')">
|
||||
Name is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Target Path</mat-label>
|
||||
<input matInput formControlName="TargetPath">
|
||||
<mat-error *ngIf="group.controls.TargetPath.hasError('required')">
|
||||
TargetPath is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Metadata Path</mat-label>
|
||||
<input matInput formControlName="MetadataPath">
|
||||
<mat-error *ngIf="group.controls.MetadataPath.hasError('required')">
|
||||
MetadataPath is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Repository Name</mat-label>
|
||||
<input matInput formControlName="RepoName" readonly>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>URL</mat-label>
|
||||
<input matInput formControlName="URL">
|
||||
<mat-error *ngIf="group.controls.URL.hasError('required')">
|
||||
URL is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-label>
|
||||
<mat-select [(value)]="checkoutType">
|
||||
<mat-option *ngFor="let type of checkoutTypes" [value]="type">{{type}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-label>
|
||||
<mat-form-field appearance="fill" *ngIf="checkoutType === 'Branch'">
|
||||
<input matInput formControlName="Branch">
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" *ngIf="checkoutType === 'Tag'">
|
||||
<input matInput formControlName="Tag">
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" *ngIf="checkoutType === 'CommitHash'">
|
||||
<input matInput formControlName="CommitHash">
|
||||
</mat-form-field>
|
||||
<p>
|
||||
<mat-checkbox formControlName="Force" labelPosition="after">Force</mat-checkbox>
|
||||
</p>
|
||||
<p>
|
||||
<mat-checkbox formControlName="IsPhase" labelPosition="after">Is Phase</mat-checkbox>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div mat-dialog-actions>
|
||||
<button mat-raised-button (click)="closeDialog()">Cancel</button>
|
||||
<button mat-raised-button color="primary" (click)="setConfig(data.formName)">Save</button>
|
||||
<button mat-raised-button color="primary" [disabled]="!group.valid" (click)="setConfig()">Save</button>
|
||||
</div>
|
@ -18,6 +18,7 @@ import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatDialogModule, MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ToastrModule } from 'ngx-toastr';
|
||||
import { ConfigNewComponent } from './config-new.component';
|
||||
@ -36,6 +37,7 @@ describe('ConfigNewComponent', () => {
|
||||
MatInputModule,
|
||||
MatDialogModule,
|
||||
MatCheckboxModule,
|
||||
MatSelectModule,
|
||||
ToastrModule.forRoot(),
|
||||
],
|
||||
declarations: [ ConfigNewComponent ],
|
||||
@ -52,6 +54,11 @@ describe('ConfigNewComponent', () => {
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.data.formName = 'context';
|
||||
component.data.configs = {
|
||||
manifests: ['default'],
|
||||
encryption: ['default'],
|
||||
management: ['default']
|
||||
};
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
|
@ -13,11 +13,10 @@
|
||||
*/
|
||||
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { WsService } from 'src/services/ws/ws.service';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { ContextOptions, EncryptionConfigOptions, ManagementConfig, ManifestOptions } from '../config.models';
|
||||
import { WsConstants, WsMessage } from 'src/services/ws/ws.models';
|
||||
|
||||
@Component({
|
||||
@ -27,77 +26,108 @@ import { WsConstants, WsMessage } from 'src/services/ws/ws.models';
|
||||
})
|
||||
export class ConfigNewComponent implements OnInit {
|
||||
group: FormGroup;
|
||||
|
||||
dataObj: any;
|
||||
keys: string[] = [];
|
||||
|
||||
dataObjs = {
|
||||
context: new ContextOptions(),
|
||||
manifest: new ManifestOptions(),
|
||||
encryption: new EncryptionConfigOptions(),
|
||||
management: new ManagementConfig()
|
||||
};
|
||||
subComponent: string;
|
||||
encryptionType: string;
|
||||
checkoutTypes = ['Branch', 'Tag', 'CommitHash'];
|
||||
checkoutType = 'Branch';
|
||||
|
||||
constructor(private websocketService: WsService,
|
||||
private fb: FormBuilder,
|
||||
@Inject(MAT_DIALOG_DATA) public data: {formName: string},
|
||||
@Inject(MAT_DIALOG_DATA) public data: {
|
||||
formName: string,
|
||||
configs: {}
|
||||
},
|
||||
public dialogRef: MatDialogRef<ConfigNewComponent>) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
const grp = {};
|
||||
this.dataObj = this.dataObjs[this.data.formName];
|
||||
|
||||
for (const [key, val] of Object.entries(this.dataObj)) {
|
||||
this.keys.push(key);
|
||||
grp[key] = new FormControl(val);
|
||||
}
|
||||
|
||||
this.group = new FormGroup(grp);
|
||||
}
|
||||
|
||||
setConfig(type: string): void {
|
||||
let subComponent = '';
|
||||
|
||||
switch (type) {
|
||||
switch (this.data.formName) {
|
||||
case 'context':
|
||||
subComponent = WsConstants.SET_CONTEXT;
|
||||
this.group = this.fb.group({
|
||||
Name: new FormControl('', Validators.required),
|
||||
Manifest: new FormControl(''),
|
||||
EncryptionConfig: new FormControl(''),
|
||||
ManagementConfiguration: new FormControl('')
|
||||
});
|
||||
this.subComponent = WsConstants.SET_CONTEXT;
|
||||
break;
|
||||
case 'manifest':
|
||||
subComponent = WsConstants.SET_MANIFEST;
|
||||
this.group = this.fb.group({
|
||||
Name: new FormControl('', Validators.required),
|
||||
TargetPath: new FormControl('', Validators.required),
|
||||
MetadataPath: new FormControl('', Validators.required),
|
||||
// new manifests seem to get an auto-generated repo named 'primary'
|
||||
// that won't get configured properly unless it's done here, so
|
||||
// don't let users modify this field
|
||||
RepoName: new FormControl({value: 'primary', disabled: true}),
|
||||
URL: new FormControl('', Validators.required),
|
||||
Tag: new FormControl(''),
|
||||
CommitHash: new FormControl(''),
|
||||
Branch: new FormControl(''),
|
||||
IsPhase: new FormControl(false),
|
||||
Force: new FormControl(false)
|
||||
});
|
||||
this.subComponent = WsConstants.SET_MANIFEST;
|
||||
break;
|
||||
case 'encryption':
|
||||
subComponent = WsConstants.SET_ENCRYPTION_CONFIG;
|
||||
this.group = this.fb.group({
|
||||
Name: new FormControl('', Validators.required),
|
||||
EncryptionKeyPath: new FormControl('', Validators.required),
|
||||
DecryptionKeyPath: new FormControl('', Validators.required),
|
||||
KeySecretName: new FormControl('', Validators.required),
|
||||
KeySecretNamespace: new FormControl('', Validators.required),
|
||||
});
|
||||
this.subComponent = WsConstants.SET_ENCRYPTION_CONFIG;
|
||||
break;
|
||||
case 'management':
|
||||
subComponent = WsConstants.SET_MANAGEMENT_CONFIG;
|
||||
// NOTE: capitalizations are different for management config due to
|
||||
// inconsistent json definitions in airshipctl
|
||||
this.group = this.fb.group({
|
||||
Name: new FormControl('', Validators.required),
|
||||
type: new FormControl(''),
|
||||
insecure: new FormControl(false),
|
||||
useproxy: new FormControl(false),
|
||||
systemActionRetries: new FormControl(0, Validators.pattern('^[0-9]*$')),
|
||||
systemRebootDelay: new FormControl(0, Validators.pattern('^[0-9]*$'))
|
||||
});
|
||||
this.subComponent = WsConstants.SET_MANAGEMENT_CONFIG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const [key, control] of Object.entries(this.group.controls)) {
|
||||
// TODO(mfuller): need to validate this within the form
|
||||
if (typeof this.dataObj[key] === 'number') {
|
||||
this.dataObj[key] = +control.value;
|
||||
setConfig(): void {
|
||||
const msg = new WsMessage(WsConstants.CTL, WsConstants.CONFIG, this.subComponent);
|
||||
const opts = {};
|
||||
for (const [key, val] of Object.entries(this.group.controls)) {
|
||||
if (key === 'systemActionRetries' || key === 'systemRebootDelay') {
|
||||
opts[key] = +val.value;
|
||||
} else {
|
||||
this.dataObj[key] = control.value;
|
||||
opts[key] = val.value;
|
||||
}
|
||||
}
|
||||
|
||||
const msg = new WsMessage(WsConstants.CTL, WsConstants.CONFIG, subComponent);
|
||||
msg.data = JSON.parse(JSON.stringify(this.dataObj));
|
||||
msg.name = this.dataObj.Name;
|
||||
const name = 'Name';
|
||||
msg.name = opts[name];
|
||||
msg.data = JSON.parse(JSON.stringify(opts));
|
||||
|
||||
this.websocketService.sendMessage(msg);
|
||||
this.dialogRef.close();
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
closeDialog(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
// annoying helper method because apparently I can't just test this natively
|
||||
// inside an *ngIf
|
||||
isBool(val: any): boolean {
|
||||
return typeof val === 'boolean';
|
||||
onEncryptionChange(event: any): void {
|
||||
if (this.encryptionType === 'encryption') {
|
||||
this.group.controls.EncryptionKeyPath.enable();
|
||||
this.group.controls.DecryptionKeyPath.enable();
|
||||
this.group.controls.KeySecretName.disable();
|
||||
this.group.controls.KeySecretNamespace.disable();
|
||||
} else {
|
||||
this.group.controls.EncryptionKeyPath.disable();
|
||||
this.group.controls.DecryptionKeyPath.disable();
|
||||
this.group.controls.KeySecretName.enable();
|
||||
this.group.controls.KeySecretNamespace.enable();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,8 +19,7 @@ import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { ContextOptions, EncryptionConfigOptions, ManagementConfig, ManifestOptions } from '../config.models';
|
||||
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -31,10 +30,7 @@ import { ContextOptions, EncryptionConfigOptions, ManagementConfig, ManifestOpti
|
||||
ReactiveFormsModule,
|
||||
MatCheckboxModule,
|
||||
MatDialogModule,
|
||||
ContextOptions,
|
||||
ManifestOptions,
|
||||
ManagementConfig,
|
||||
EncryptionConfigOptions
|
||||
MatSelectModule
|
||||
],
|
||||
declarations: [
|
||||
],
|
||||
|
@ -183,7 +183,10 @@ export class ConfigComponent implements WsReceiver, OnInit {
|
||||
const dialogRef = this.dialog.open(ConfigNewComponent, {
|
||||
width: '550px',
|
||||
height: '650px',
|
||||
data: { formName: configType}
|
||||
data: {
|
||||
formName: configType,
|
||||
configs: this.configs
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user