Enhancements to configuration sharable component

This commit is contained in:
Steven Zou 2017-07-05 16:10:47 +08:00
parent f50a125d61
commit c3dd5e6d12
9 changed files with 147 additions and 42 deletions

View File

@ -100,7 +100,7 @@ export class Configuration {
this.verify_remote_cert = new BoolValueItem(false, true); this.verify_remote_cert = new BoolValueItem(false, true);
this.scan_all_policy = new ComplexValueItem({ this.scan_all_policy = new ComplexValueItem({
type: "daily", type: "daily",
parameters: { parameter: {
daily_time: 0 daily_time: 0
} }
}, true); }, true);

View File

@ -1,7 +1,12 @@
export const REGISTRY_CONFIG_HTML: string = ` export const REGISTRY_CONFIG_HTML: string = `
<div> <div>
<replication-config [(replicationConfig)]="config"></replication-config> <replication-config #replicationConfig [(replicationConfig)]="config"></replication-config>
<system-settings [(systemSettings)]="config"></system-settings> <system-settings #systemSettings [(systemSettings)]="config"></system-settings>
<vulnerability-config [(vulnerabilityConfig)]="config"></vulnerability-config> <vulnerability-config #vulnerabilityConfig [(vulnerabilityConfig)]="config"></vulnerability-config>
<div>
<button type="button" class="btn btn-primary" (click)="save()" [disabled]="shouldDisable">{{'BUTTON.SAVE' | translate}}</button>
<button type="button" class="btn btn-outline" (click)="cancel()" [disabled]="shouldDisable">{{'BUTTON.CANCEL' | translate}}</button>
</div>
<confirmation-dialog #cfgConfirmationDialog (confirmAction)="confirmCancel($event)"></confirmation-dialog>
</div> </div>
`; `;

View File

@ -8,6 +8,7 @@ import { ReplicationConfigComponent } from './replication/replication-config.com
import { SystemSettingsComponent } from './system/system-settings.component'; import { SystemSettingsComponent } from './system/system-settings.component';
import { VulnerabilityConfigComponent } from './vulnerability/vulnerability-config.component'; import { VulnerabilityConfigComponent } from './vulnerability/vulnerability-config.component';
import { RegistryConfigComponent } from './registry-config.component'; import { RegistryConfigComponent } from './registry-config.component';
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
import { import {
ConfigurationService, ConfigurationService,
@ -29,7 +30,7 @@ describe('RegistryConfigComponent (inline template)', () => {
mockConfig.verify_remote_cert.value = true; mockConfig.verify_remote_cert.value = true;
mockConfig.scan_all_policy.value = { mockConfig.scan_all_policy.value = {
type: "daily", type: "daily",
parameters: { parameter: {
daily_time: 0 daily_time: 0
} }
}; };
@ -46,7 +47,8 @@ describe('RegistryConfigComponent (inline template)', () => {
ReplicationConfigComponent, ReplicationConfigComponent,
SystemSettingsComponent, SystemSettingsComponent,
VulnerabilityConfigComponent, VulnerabilityConfigComponent,
RegistryConfigComponent RegistryConfigComponent,
ConfirmationDialogComponent
], ],
providers: [ providers: [
ErrorHandler, ErrorHandler,

View File

@ -1,10 +1,22 @@
import { Component, OnInit, EventEmitter, Output } from '@angular/core'; import { Component, OnInit, EventEmitter, Output, ViewChild } from '@angular/core';
import { Configuration, ComplexValueItem } from './config'; import { Configuration, ComplexValueItem } from './config';
import { REGISTRY_CONFIG_HTML } from './registry-config.component.html'; import { REGISTRY_CONFIG_HTML } from './registry-config.component.html';
import { ConfigurationService } from '../service/index'; import { ConfigurationService } from '../service/index';
import { toPromise } from '../utils'; import { toPromise } from '../utils';
import { ErrorHandler } from '../error-handler'; import { ErrorHandler } from '../error-handler';
import {
ReplicationConfigComponent,
SystemSettingsComponent,
VulnerabilityConfigComponent
} from './index';
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const';
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message';
import { ConfirmationAcknowledgement } from '../confirmation-dialog/confirmation-state-message';
import { TranslateService } from '@ngx-translate/core';
@Component({ @Component({
selector: 'hbr-registry-config', selector: 'hbr-registry-config',
@ -13,27 +25,56 @@ import { ErrorHandler } from '../error-handler';
export class RegistryConfigComponent implements OnInit { export class RegistryConfigComponent implements OnInit {
config: Configuration = new Configuration(); config: Configuration = new Configuration();
configCopy: Configuration; configCopy: Configuration;
onGoing: boolean = false;
@Output() configChanged: EventEmitter<any> = new EventEmitter<any>(); @ViewChild("replicationConfig") replicationCfg: ReplicationConfigComponent;
@ViewChild("systemSettings") systemSettings: SystemSettingsComponent;
@ViewChild("vulnerabilityConfig") vulnerabilityCfg: VulnerabilityConfigComponent;
@ViewChild("cfgConfirmationDialog") confirmationDlg: ConfirmationDialogComponent;
constructor( constructor(
private configService: ConfigurationService, private configService: ConfigurationService,
private errorHandler: ErrorHandler private errorHandler: ErrorHandler,
private translate: TranslateService
) { } ) { }
get shouldDisable(): boolean {
return !this.isValid() || !this.hasChanges() || this.onGoing;
}
ngOnInit(): void { ngOnInit(): void {
//Initialize //Initialize
this.load(); this.load();
} }
isValid(): boolean {
return this.replicationCfg &&
this.replicationCfg.isValid &&
this.systemSettings &&
this.systemSettings.isValid &&
this.vulnerabilityCfg &&
this.vulnerabilityCfg.isValid;
}
hasChanges(): boolean {
return !this._isEmptyObject(this.getChanges());
}
//Load configurations //Load configurations
load(): void { load(): void {
this.onGoing = true;
toPromise<Configuration>(this.configService.getConfigurations()) toPromise<Configuration>(this.configService.getConfigurations())
.then((config: Configuration) => { .then((config: Configuration) => {
this.configCopy = Object.assign({}, config); this.onGoing = false;
this.configCopy = this._clone(config);
this.config = config; this.config = config;
}) })
.catch(error => this.errorHandler.error(error)); .catch(error => {
this.onGoing = false;
this.errorHandler.error(error);
});
} }
//Save configuration changes //Save configuration changes
@ -45,26 +86,48 @@ export class RegistryConfigComponent implements OnInit {
return; return;
} }
//Fix policy parameters issue this.onGoing = true;
let scanningAllPolicy = changes["scan_all_policy"];
if (scanningAllPolicy &&
scanningAllPolicy.type !== "daily" &&
scanningAllPolicy.parameters) {
delete (scanningAllPolicy.parameters);
}
toPromise<any>(this.configService.saveConfigurations(changes)) toPromise<any>(this.configService.saveConfigurations(changes))
.then(() => { .then(() => {
this.configChanged.emit(changes); this.onGoing = false;
this.translate.get("CONFIG.SAVE_SUCCESS").subscribe((res: string) => {
this.errorHandler.info(res);
});
//Reload to fetch all the updates
this.load();
}) })
.catch(error => this.errorHandler.error(error)); .catch(error => {
this.onGoing = false;
this.errorHandler.error(error);
});
}
//Cancel the changes if have
cancel(): void {
let msg = new ConfirmationMessage(
"CONFIG.CONFIRM_TITLE",
"CONFIG.CONFIRM_SUMMARY",
"",
{},
ConfirmationTargets.CONFIG
);
this.confirmationDlg.open(msg);
}
//Confirm cancel
confirmCancel(ack: ConfirmationAcknowledgement): void {
if (ack && ack.source === ConfirmationTargets.CONFIG &&
ack.state === ConfirmationState.CONFIRMED) {
this.reset();
}
} }
reset(): void { reset(): void {
//Reset to the values of copy //Reset to the values of copy
let changes: { [key: string]: any | any[] } = this.getChanges(); let changes: { [key: string]: any | any[] } = this.getChanges();
for (let prop in changes) { for (let prop in changes) {
this.config[prop] = Object.assign({}, this.configCopy[prop]); this.config[prop] = this._clone(this.configCopy[prop]);
} }
} }
@ -107,4 +170,10 @@ export class RegistryConfigComponent implements OnInit {
_isEmptyObject(obj: any): boolean { _isEmptyObject(obj: any): boolean {
return !obj || JSON.stringify(obj) === "{}"; return !obj || JSON.stringify(obj) === "{}";
} }
//Deeper clone all
_clone(srcObj: any): any {
if (!srcObj) return null;
return JSON.parse(JSON.stringify(srcObj));
}
} }

View File

@ -5,7 +5,7 @@ export const VULNERABILITY_CONFIG_HTML: string = `
<div class="form-group"> <div class="form-group">
<label for="scanAllPolicy">{{ 'CONFIG.SCANNING.SCAN_ALL' | translate }}</label> <label for="scanAllPolicy">{{ 'CONFIG.SCANNING.SCAN_ALL' | translate }}</label>
<div class="select"> <div class="select">
<select id="scanAllPolicy" name="scanAllPolicy" [disabled]="!editable" [(ngModel)]="vulnerabilityConfig.scan_all_policy.value.type"> <select id="scanAllPolicy" name="scanAllPolicy" [disabled]="!editable" [(ngModel)]="scanningType">
<option value="none">{{ 'CONFIG.SCANNING.NONE_POLICY' | translate }}</option> <option value="none">{{ 'CONFIG.SCANNING.NONE_POLICY' | translate }}</option>
<option value="daily">{{ 'CONFIG.SCANNING.DAILY_POLICY' | translate }}</option> <option value="daily">{{ 'CONFIG.SCANNING.DAILY_POLICY' | translate }}</option>
<option value="on_refresh">{{ 'CONFIG.SCANNING.REFRESH_POLICY' | translate }}</option> <option value="on_refresh">{{ 'CONFIG.SCANNING.REFRESH_POLICY' | translate }}</option>

View File

@ -30,13 +30,13 @@ export class VulnerabilityConfigComponent {
this.config = cfg; this.config = cfg;
if (this.config.scan_all_policy && if (this.config.scan_all_policy &&
this.config.scan_all_policy.value) { this.config.scan_all_policy.value) {
if (this.config.scan_all_policy.value.type === "daily"){ if (this.config.scan_all_policy.value.type === "daily") {
if(!this.config.scan_all_policy.value.parameters){ if (!this.config.scan_all_policy.value.parameter) {
this.config.scan_all_policy.value.parameters = { this.config.scan_all_policy.value.parameter = {
daily_time: 0 daily_time: 0
}; };
}
} }
}
} }
this.configChange.emit(this.config); this.configChange.emit(this.config);
} }
@ -51,8 +51,8 @@ export class VulnerabilityConfigComponent {
} }
let timeOffset: number = 0;//seconds let timeOffset: number = 0;//seconds
if (this.config.scan_all_policy.value.parameters) { if (this.config.scan_all_policy.value.parameter) {
let daily_time = this.config.scan_all_policy.value.parameters.daily_time; let daily_time = this.config.scan_all_policy.value.parameter.daily_time;
if (daily_time && typeof daily_time === "number") { if (daily_time && typeof daily_time === "number") {
timeOffset = +daily_time; timeOffset = +daily_time;
} }
@ -99,8 +99,9 @@ export class VulnerabilityConfigComponent {
return; return;
} }
if (!this.config.scan_all_policy.value.parameters) { //Double confirm inner parameter existing.
this.config.scan_all_policy.value.parameters = { if (!this.config.scan_all_policy.value.parameter) {
this.config.scan_all_policy.value.parameter = {
daily_time: 0 daily_time: 0
}; };
} }
@ -124,7 +125,41 @@ export class VulnerabilityConfigComponent {
utcTimes -= ONE_DAY_SECONDS; utcTimes -= ONE_DAY_SECONDS;
} }
this.config.scan_all_policy.value.parameters.daily_time = utcTimes; this.config.scan_all_policy.value.parameter.daily_time = utcTimes;
}
//Scanning type
get scanningType(): string {
if (this.config &&
this.config.scan_all_policy &&
this.config.scan_all_policy.value) {
return this.config.scan_all_policy.value.type;
} else {
//default
return "none";
}
}
set scanningType(v: string) {
if (this.config &&
this.config.scan_all_policy &&
this.config.scan_all_policy.value) {
let type: string = (v && v.trim() !== "") ? v : "none";
this.config.scan_all_policy.value.type = type;
if (type !== "daily") {
//No parameter
if (this.config.scan_all_policy.value.parameter) {
delete (this.config.scan_all_policy.value.parameter);
}
} else {
//Has parameter
if (!this.config.scan_all_policy.value.parameter) {
this.config.scan_all_policy.value.parameter = {
daily_time: 0
};
}
}
}
} }
@ViewChild("systemConfigFrom") systemSettingsForm: NgForm; @ViewChild("systemConfigFrom") systemSettingsForm: NgForm;

View File

@ -31,7 +31,7 @@
"clarity-icons": "^0.9.8", "clarity-icons": "^0.9.8",
"clarity-ui": "^0.9.8", "clarity-ui": "^0.9.8",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"harbor-ui": "^0.2.40", "harbor-ui": "^0.2.52",
"intl": "^1.2.5", "intl": "^1.2.5",
"mutationobserver-shim": "^0.3.2", "mutationobserver-shim": "^0.3.2",
"ngx-cookie": "^1.0.0", "ngx-cookie": "^1.0.0",

View File

@ -16,7 +16,7 @@
<button id="config-system" class="btn btn-link nav-link" aria-controls="system_settings" [class.active]='isCurrentTabLink("config-system")' type="button" (click)='tabLinkClick("config-system")'>{{'CONFIG.SYSTEM' | translate }}</button> <button id="config-system" class="btn btn-link nav-link" aria-controls="system_settings" [class.active]='isCurrentTabLink("config-system")' type="button" (click)='tabLinkClick("config-system")'>{{'CONFIG.SYSTEM' | translate }}</button>
</li> </li>
<li role="presentation" class="nav-item"> <li role="presentation" class="nav-item">
<button id="config-vulnerability" class="btn btn-link nav-link" aria-controls="vulnerability" [class.active]='isCurrentTabLink("config-vulnerability")' type="button" (click)='tabLinkClick("config-vulnerability")'>Vulnerability</button> <button id="config-vulnerability" class="btn btn-link nav-link" aria-controls="vulnerability" [class.active]='isCurrentTabLink("config-vulnerability")' type="button" (click)='tabLinkClick("config-vulnerability")'>{{'VULNERABILITY.SINGULAR' | translate}}</button>
</li> </li>
</ul> </ul>
<section id="authentication" role="tabpanel" aria-labelledby="config-auth" [hidden]='!isCurrentTabContent("authentication")'> <section id="authentication" role="tabpanel" aria-labelledby="config-auth" [hidden]='!isCurrentTabContent("authentication")'>
@ -41,7 +41,6 @@
<button type="button" class="btn btn-outline" (click)="testLDAPServer()" *ngIf="showLdapServerBtn" [disabled]="!isLDAPConfigValid()">{{'BUTTON.TEST_LDAP' | translate}}</button> <button type="button" class="btn btn-outline" (click)="testLDAPServer()" *ngIf="showLdapServerBtn" [disabled]="!isLDAPConfigValid()">{{'BUTTON.TEST_LDAP' | translate}}</button>
<span id="forTestingMail" class="spinner spinner-inline" [hidden]="hideMailTestingSpinner"></span> <span id="forTestingMail" class="spinner spinner-inline" [hidden]="hideMailTestingSpinner"></span>
<span id="forTestingLDAP" class="spinner spinner-inline" [hidden]="hideLDAPTestingSpinner"></span> <span id="forTestingLDAP" class="spinner spinner-inline" [hidden]="hideLDAPTestingSpinner"></span>
<button type="button" class="btn btn-primary" (click)="consoleTest()">CONSOLE</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -71,11 +71,6 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
private appConfigService: AppConfigService, private appConfigService: AppConfigService,
private session: SessionService) { } private session: SessionService) { }
consoleTest(): void {
console.log(this.allConfig, this.originalCopy);
console.log("-------------");
console.log(this.getChanges());
}
isCurrentTabLink(tabId: string): boolean { isCurrentTabLink(tabId: string): boolean {
return this.currentTabId === tabId; return this.currentTabId === tabId;
} }