diff --git a/client/src/app/ctl/config/config-manifest/config-manifest.component.ts b/client/src/app/ctl/config/config-manifest/config-manifest.component.ts index 6ea505b..b7a3037 100644 --- a/client/src/app/ctl/config/config-manifest/config-manifest.component.ts +++ b/client/src/app/ctl/config/config-manifest/config-manifest.component.ts @@ -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); } diff --git a/client/src/app/ctl/config/config-new/config-new.component.css b/client/src/app/ctl/config/config-new/config-new.component.css index 9c0b686..c9f9cbd 100644 --- a/client/src/app/ctl/config/config-new/config-new.component.css +++ b/client/src/app/ctl/config/config-new/config-new.component.css @@ -19,3 +19,7 @@ .text-input { width: 80%; } + +mat-form-field { + width: 80%; +} diff --git a/client/src/app/ctl/config/config-new/config-new.component.html b/client/src/app/ctl/config/config-new/config-new.component.html index 7849b16..af0f19c 100644 --- a/client/src/app/ctl/config/config-new/config-new.component.html +++ b/client/src/app/ctl/config/config-new/config-new.component.html @@ -1,18 +1,171 @@

New {{data.formName}} configuration

-
-
-
- - {{key}} - +
+
+ + Name + + + Name is required + + + + Type + + + + System Action Retries + + + Value must be a number + + + + System Reboot Delay + + + Value must be a number + + +

+ Use Proxy +

+

+ Insecure +

+
+
+ + Name + + + Name is required + +
+ + Manifest + + {{m}} + +
+ + Encryption Config + + + None + {{e}} + +
+ + Management Config + + {{m}} + + +
+
+ + Name + + + Name is required + + + + Config Type + + Encrypt / Decrypt Key + Secret + + +
+ + EncryptionKeyPath + + + EncryptionKeyPath is required + + + + DecryptionKeyPath + + + DecryptionKeyPath is required + -

- {{key}} -

- +
+ + KeySecretName + + + KeySecretName is required + + + + KeySecretNamespace + + + KeySecretNamespace is required + + +
+
+
+ + Name + + + Name is required + + + + Target Path + + + TargetPath is required + + + + Metadata Path + + + MetadataPath is required + + + + Repository Name + + + + URL + + + URL is required + + + + + {{type}} + + + + + + + + + + + +

+ Force +

+

+ Is Phase +

+
- +
\ No newline at end of file diff --git a/client/src/app/ctl/config/config-new/config-new.component.spec.ts b/client/src/app/ctl/config/config-new/config-new.component.spec.ts index e4af196..56729ca 100644 --- a/client/src/app/ctl/config/config-new/config-new.component.spec.ts +++ b/client/src/app/ctl/config/config-new/config-new.component.spec.ts @@ -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(); }); diff --git a/client/src/app/ctl/config/config-new/config-new.component.ts b/client/src/app/ctl/config/config-new/config-new.component.ts index 01e50ca..e598057 100644 --- a/client/src/app/ctl/config/config-new/config-new.component.ts +++ b/client/src/app/ctl/config/config-new/config-new.component.ts @@ -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) { } 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(); + } } } diff --git a/client/src/app/ctl/config/config-new/config-new.module.ts b/client/src/app/ctl/config/config-new/config-new.module.ts index 3f7ee23..4aa5528 100644 --- a/client/src/app/ctl/config/config-new/config-new.module.ts +++ b/client/src/app/ctl/config/config-new/config-new.module.ts @@ -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: [ ], diff --git a/client/src/app/ctl/config/config.component.ts b/client/src/app/ctl/config/config.component.ts index f30a2ac..3844bdb 100755 --- a/client/src/app/ctl/config/config.component.ts +++ b/client/src/app/ctl/config/config.component.ts @@ -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 + } }); } }