This commit is contained in:
Steven Zou 2017-07-27 18:39:29 +08:00
parent 9582637651
commit 5f548ea49a
10 changed files with 533 additions and 400 deletions

View File

@ -3,7 +3,12 @@ import { Component, OnInit, EventEmitter, Output, ViewChild, Input } from '@angu
import { Configuration, ComplexValueItem } from './config';
import { REGISTRY_CONFIG_HTML } from './registry-config.component.html';
import { ConfigurationService, SystemInfoService, SystemInfo, ClairDBStatus } from '../service/index';
import { toPromise } from '../utils';
import {
toPromise,
compareValue,
isEmptyObject,
clone
} from '../utils';
import { ErrorHandler } from '../error-handler';
import {
ReplicationConfigComponent,
@ -70,7 +75,7 @@ export class RegistryConfigComponent implements OnInit {
}
hasChanges(): boolean {
return !this._isEmptyObject(this.getChanges());
return !isEmptyObject(this.getChanges());
}
//Get system info
@ -85,7 +90,7 @@ export class RegistryConfigComponent implements OnInit {
this.onGoing = true;
toPromise<Configuration>(this.configService.getConfigurations())
.then((config: Configuration) => {
this.configCopy = this._clone(config);
this.configCopy = clone(config);
this.config = config;
this.onGoing = false;
})
@ -99,7 +104,7 @@ export class RegistryConfigComponent implements OnInit {
save(): void {
let changes: { [key: string]: any | any[] } = this.getChanges();
if (this._isEmptyObject(changes)) {
if (isEmptyObject(changes)) {
//Guard code, do nothing
return;
}
@ -147,7 +152,7 @@ export class RegistryConfigComponent implements OnInit {
//Reset to the values of copy
let changes: { [key: string]: any | any[] } = this.getChanges();
for (let prop in changes) {
this.config[prop] = this._clone(this.configCopy[prop]);
this.config[prop] = clone(this.configCopy[prop]);
}
}
@ -160,7 +165,7 @@ export class RegistryConfigComponent implements OnInit {
for (let prop in this.config) {
let field = this.configCopy[prop];
if (field && field.editable) {
if (!this._compareValue(field.value, this.config[prop].value)) {
if (!compareValue(field.value, this.config[prop].value)) {
changes[prop] = this.config[prop].value;
//Number
if (typeof field.value === "number") {
@ -177,23 +182,4 @@ export class RegistryConfigComponent implements OnInit {
return changes;
}
//private
_compareValue(a: any, b: any): boolean {
if ((a && !b) || (!a && b)) return false;
if (!a && !b) return true;
return JSON.stringify(a) === JSON.stringify(b);
}
//private
_isEmptyObject(obj: any): boolean {
return !obj || JSON.stringify(obj) === "{}";
}
//Deeper clone all
_clone(srcObj: any): any {
if (!srcObj) return null;
return JSON.parse(JSON.stringify(srcObj));
}
}

View File

@ -1,4 +1,5 @@
export const CREATE_EDIT_ENDPOINT_TEMPLATE: string = `<clr-modal [(clrModalOpen)]="createEditDestinationOpened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
export const CREATE_EDIT_ENDPOINT_TEMPLATE: string = `
<clr-modal [(clrModalOpen)]="createEditDestinationOpened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
<h3 class="modal-title">{{modalTitle}}</h3>
<hbr-inline-alert class="modal-title" (confirmEvt)="confirmCancel($event)"></hbr-inline-alert>
<div class="modal-body">
@ -12,7 +13,7 @@ export const CREATE_EDIT_ENDPOINT_TEMPLATE: string = `<clr-modal [(clrModalOpen)
<form #targetForm="ngForm">
<section class="form-block">
<div class="form-group">
<label for="destination_name" class="col-md-4 form-group-label-override">{{ 'DESTINATION.NAME' | translate }}<span style="color: red">*</span></label>
<label for="destination_name" class="col-md-4 form-group-label-override required">{{ 'DESTINATION.NAME' | translate }}</label>
<label class="col-md-8" for="destination_name" aria-haspopup="true" role="tooltip" [class.invalid]="targetName.errors && (targetName.dirty || targetName.touched)" [class.valid]="targetName.valid" class="tooltip tooltip-validation tooltip-sm tooltip-bottom-left">
<input type="text" id="destination_name" [disabled]="testOngoing" [readonly]="!editable" [(ngModel)]="target.name" name="targetName" size="20" #targetName="ngModel" required (keyup)="changedTargetName($event)">
<span class="tooltip-content" *ngIf="targetName.errors && targetName.errors.required && (targetName.dirty || targetName.touched)">
@ -21,9 +22,9 @@ export const CREATE_EDIT_ENDPOINT_TEMPLATE: string = `<clr-modal [(clrModalOpen)
</label>
</div>
<div class="form-group">
<label for="destination_url" class="col-md-4 form-group-label-override">{{ 'DESTINATION.URL' | translate }}<span style="color: red">*</span></label>
<label for="destination_url" class="col-md-4 form-group-label-override required">{{ 'DESTINATION.URL' | translate }}</label>
<label class="col-md-8" for="destination_url" aria-haspopup="true" role="tooltip" [class.invalid]="targetEndpoint.errors && (targetEndpoint.dirty || targetEndpoint.touched)" [class.valid]="targetEndpoint.valid" class="tooltip tooltip-validation tooltip-sm tooltip-bottom-left">
<input type="text" id="destination_url" [disabled]="testOngoing" [readonly]="!editable" [(ngModel)]="target.endpoint" size="20" name="endpointUrl" #targetEndpoint="ngModel" required (keyup)="clearPassword($event)">
<input type="text" id="destination_url" [disabled]="testOngoing" [readonly]="!editable" [(ngModel)]="target.endpoint" size="20" name="endpointUrl" #targetEndpoint="ngModel" required (keyup)="clearPassword($event)" placeholder="http(s)://192.168.1.1">
<span class="tooltip-content" *ngIf="targetEndpoint.errors && targetEndpoint.errors.required && (targetEndpoint.dirty || targetEndpoint.touched)">
{{ 'DESTINATION.URL_IS_REQUIRED' | translate }}
</span>
@ -31,7 +32,7 @@ export const CREATE_EDIT_ENDPOINT_TEMPLATE: string = `<clr-modal [(clrModalOpen)
</div>
<div class="form-group">
<label for="destination_username" class="col-md-4 form-group-label-override">{{ 'DESTINATION.USERNAME' | translate }}</label>
<input type="text" class="col-md-8" id="destination_username" [disabled]="testOngoing" [readonly]="!editable" [(ngModel)]="target.username" size="20" name="username" #username="ngModel" (keyup)="clearPassword($event)">
<input type="text" class="col-md-8" id="destination_username" [disabled]="testOngoing" [readonly]="!editable" [(ngModel)]="target.username" size="20" name="username" #username="ngModel" (keyup)="clearPassword($event)">
</div>
<div class="form-group">
<label for="destination_password" class="col-md-4 form-group-label-override">{{ 'DESTINATION.PASSWORD' | translate }}</label>
@ -39,14 +40,14 @@ export const CREATE_EDIT_ENDPOINT_TEMPLATE: string = `<clr-modal [(clrModalOpen)
</div>
<div class="form-group">
<label for="spin" class="col-md-4"></label>
<span class="col-md-8 spinner spinner-inline" [hidden]="!testOngoing"></span>
<span class="col-md-8 spinner spinner-inline" [hidden]="!inProgress"></span>
</div>
</section>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline" (click)="testConnection()" [disabled]="testOngoing || targetEndpoint.errors">{{ 'DESTINATION.TEST_CONNECTION' | translate }}</button>
<button type="button" class="btn btn-outline" (click)="onCancel()" [disabled]="testOngoing">{{ 'BUTTON.CANCEL' | translate }}</button>
<button type="submit" class="btn btn-primary" (click)="onSubmit()" [disabled]="testOngoing || targetForm.form.invalid || !editable">{{ 'BUTTON.OK' | translate }}</button>
<button type="button" class="btn btn-outline" (click)="testConnection()" [disabled]="testOngoing || onGoing || targetEndpoint.errors">{{ 'DESTINATION.TEST_CONNECTION' | translate }}</button>
<button type="button" class="btn btn-outline" (click)="onCancel()" [disabled]="testOngoing || onGoing">{{ 'BUTTON.CANCEL' | translate }}</button>
<button type="submit" class="btn btn-primary" (click)="onSubmit()" [disabled]="!isValid">{{ 'BUTTON.OK' | translate }}</button>
</div>
</clr-modal>`;

View File

@ -12,11 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import {
Component,
Output,
EventEmitter,
ViewChild,
AfterViewChecked
Component,
Output,
EventEmitter,
ViewChild,
AfterViewChecked,
ChangeDetectorRef,
OnDestroy
} from '@angular/core';
import { NgForm } from '@angular/forms';
@ -33,255 +35,338 @@ import { TranslateService } from '@ngx-translate/core';
import { CREATE_EDIT_ENDPOINT_STYLE } from './create-edit-endpoint.component.css';
import { CREATE_EDIT_ENDPOINT_TEMPLATE } from './create-edit-endpoint.component.html';
import { toPromise, clone, compareValue } from '../utils';
import { toPromise } from '../utils';
import { Subscription } from 'rxjs/Subscription';
const FAKE_PASSWORD = 'rjGcfuRu';
@Component({
selector: 'create-edit-endpoint',
template: CREATE_EDIT_ENDPOINT_TEMPLATE,
styles: [CREATE_EDIT_ENDPOINT_STYLE]
selector: 'create-edit-endpoint',
template: CREATE_EDIT_ENDPOINT_TEMPLATE,
styles: [CREATE_EDIT_ENDPOINT_STYLE]
})
export class CreateEditEndpointComponent implements AfterViewChecked {
export class CreateEditEndpointComponent implements AfterViewChecked, OnDestroy {
modalTitle: string;
createEditDestinationOpened: boolean;
staticBackdrop: boolean = true;
closable: boolean = false;
modalTitle: string;
createEditDestinationOpened: boolean;
editable: boolean;
testOngoing: boolean;
actionType: ActionType;
editable: boolean;
actionType: ActionType;
target: Endpoint = this.initEndpoint();
initVal: Endpoint;
target: Endpoint = this.initEndpoint;
initVal: Endpoint = this.initEndpoint;
targetForm: NgForm;
@ViewChild('targetForm')
currentForm: NgForm;
targetForm: NgForm;
endpointHasChanged: boolean;
targetNameHasChanged: boolean;
staticBackdrop: boolean = true;
closable: boolean = false;
testOngoing: boolean;
onGoing: boolean;
@ViewChild('targetForm')
currentForm: NgForm;
@ViewChild(InlineAlertComponent)
inlineAlert: InlineAlertComponent;
hasChanged: boolean;
endpointHasChanged: boolean;
targetNameHasChanged: boolean;
@Output() reload = new EventEmitter<boolean>();
@ViewChild(InlineAlertComponent)
inlineAlert: InlineAlertComponent;
timerHandler: any;
valueChangesSub: Subscription;
formValues: { [key: string]: string } | any;
@Output() reload = new EventEmitter<boolean>();
constructor(
private endpointService: EndpointService,
private errorHandler: ErrorHandler,
private translateService: TranslateService,
private ref: ChangeDetectorRef
) { }
get initEndpoint(): Endpoint {
return {
endpoint: "",
name: "",
username: "",
password: "",
type: 0
};
}
constructor(
private endpointService: EndpointService,
private errorHandler: ErrorHandler,
private translateService: TranslateService) { }
openCreateEditTarget(editable: boolean, targetId?: number | string) {
this.target = this.initEndpoint;
this.editable = editable;
this.createEditDestinationOpened = true;
this.hasChanged = false;
this.endpointHasChanged = false;
this.targetNameHasChanged = false;
this.testOngoing = false;
if (targetId) {
this.actionType = ActionType.EDIT;
this.translateService.get('DESTINATION.TITLE_EDIT').subscribe(res => this.modalTitle = res);
toPromise<Endpoint>(this.endpointService
.getEndpoint(targetId))
.then(
target => {
this.target = target;
this.initVal.name = this.target.name;
this.initVal.endpoint = this.target.endpoint;
this.initVal.username = this.target.username;
this.initVal.password = FAKE_PASSWORD;
this.target.password = this.initVal.password;
})
.catch(error => this.errorHandler.error(error));
} else {
this.actionType = ActionType.ADD_NEW;
this.translateService.get('DESTINATION.TITLE_ADD').subscribe(res => this.modalTitle = res);
}
}
testConnection() {
let payload: Endpoint = this.initEndpoint;
if (this.endpointHasChanged) {
payload.endpoint = this.target.endpoint;
payload.username = this.target.username;
payload.password = this.target.password;
} else {
payload.id = this.target.id;
public get hasChanged(): boolean {
if (this.actionType === ActionType.ADD_NEW) {
//Create new
return this.target && (
(this.target.endpoint && this.target.endpoint.trim() !== "") ||
(this.target.name && this.target.name.trim() !== "") ||
(this.target.username && this.target.username.trim() !== "") ||
(this.target.password && this.target.password.trim() !== ""));
} else {
//Edit
return !compareValue(this.target, this.initVal);
}
}
this.testOngoing = true;
toPromise<Endpoint>(this.endpointService
.pingEndpoint(payload))
.then(
response => {
this.testOngoing = false;
this.inlineAlert.showInlineSuccess({ message: "DESTINATION.TEST_CONNECTION_SUCCESS" });
}).catch(
error => {
this.testOngoing = false;
this.inlineAlert.showInlineError('DESTINATION.TEST_CONNECTION_FAILURE');
});
}
changedTargetName($event: any) {
if (this.editable) {
this.targetNameHasChanged = true;
public get isValid(): boolean {
return !this.testOngoing &&
!this.onGoing &&
this.targetForm &&
this.targetForm.valid &&
this.editable;
}
}
clearPassword($event: any) {
if (this.editable) {
this.target.password = '';
this.endpointHasChanged = true;
public get inProgress(): boolean {
return this.onGoing || this.testOngoing;
}
}
onSubmit() {
switch (this.actionType) {
case ActionType.ADD_NEW:
this.addEndpoint();
break;
case ActionType.EDIT:
this.updateEndpoint();
break;
ngOnDestroy(): void {
if (this.valueChangesSub) {
this.valueChangesSub.unsubscribe();
}
}
}
addEndpoint() {
toPromise<number>(this.endpointService
.createEndpoint(this.target))
.then(
response => {
this.translateService.get('DESTINATION.CREATED_SUCCESS')
.subscribe(res => this.errorHandler.info(res));
initEndpoint(): Endpoint {
return {
endpoint: "",
name: "",
username: "",
password: "",
type: 0
};
}
open(): void {
this.createEditDestinationOpened = true;
}
close(): void {
this.createEditDestinationOpened = false;
this.reload.emit(true);
})
.catch(
error => {
let errorMessageKey = this.handleErrorMessageKey(error.status);
this.translateService
.get(errorMessageKey)
.subscribe(res => {
this.inlineAlert.showInlineError(res);
});
}
);
}
updateEndpoint() {
if (!(this.targetNameHasChanged || this.endpointHasChanged)) {
this.createEditDestinationOpened = false;
return;
}
let payload: Endpoint = this.initEndpoint;
if (this.targetNameHasChanged) {
payload.name = this.target.name;
delete payload.endpoint;
}
if (this.endpointHasChanged) {
payload.endpoint = this.target.endpoint;
payload.username = this.target.username;
payload.password = this.target.password;
delete payload.name;
}
if (!this.target.id) { return; }
toPromise<number>(this.endpointService
.updateEndpoint(this.target.id, payload))
.then(
response => {
this.translateService.get('DESTINATION.UPDATED_SUCCESS')
.subscribe(res => this.errorHandler.info(res));
this.createEditDestinationOpened = false;
this.reload.emit(true);
})
.catch(
error => {
let errorMessageKey = this.handleErrorMessageKey(error.status);
this.translateService
.get(errorMessageKey)
.subscribe(res => {
this.inlineAlert.showInlineError(res);
});
}
);
}
reset(): void {
//Reset status variables
this.endpointHasChanged = false;
this.targetNameHasChanged = false;
this.testOngoing = false;
this.onGoing = false;
handleErrorMessageKey(status: number): string {
switch (status) {
case 409: this
return 'DESTINATION.CONFLICT_NAME';
case 400:
return 'DESTINATION.INVALID_NAME';
default:
return 'UNKNOWN_ERROR';
//Reset data
this.target = this.initEndpoint();
this.initVal = this.initEndpoint();
this.formValues = null;
}
}
onCancel() {
if (this.hasChanged) {
this.inlineAlert.showInlineConfirmation({ message: 'ALERT.FORM_CHANGE_CONFIRMATION' });
} else {
this.createEditDestinationOpened = false;
if (this.targetForm)
this.targetForm.reset();
}
}
confirmCancel(confirmed: boolean) {
this.createEditDestinationOpened = false;
this.inlineAlert.close();
}
ngAfterViewChecked(): void {
this.targetForm = this.currentForm;
if (this.targetForm) {
let comparison: { [key: string]: any } = {
targetName: this.initVal.name,
endpointUrl: this.initVal.endpoint,
username: this.initVal.username,
password: this.initVal.password
};
let self: CreateEditEndpointComponent | any = this;
if (self) {
self.targetForm.valueChanges.subscribe((data: any) => {
for (let key in data) {
let current = data[key];
let origin: string = comparison[key];
if (((this.actionType === ActionType.EDIT && this.editable && !current) || current) &&
current !== origin) {
this.hasChanged = true;
break;
} else {
this.hasChanged = false;
this.inlineAlert.close();
//Forcely refresh the view
forceRefreshView(duration: number): void {
//Reset timer
if (this.timerHandler) {
clearInterval(this.timerHandler);
}
this.timerHandler = setInterval(() => this.ref.markForCheck(), 100);
setTimeout(() => {
if (this.timerHandler) {
clearInterval(this.timerHandler);
this.timerHandler = null;
}
}
});
}
}, duration);
}
openCreateEditTarget(editable: boolean, targetId?: number | string) {
this.editable = editable;
//reset
this.reset();
if (targetId) {
this.actionType = ActionType.EDIT;
this.translateService.get('DESTINATION.TITLE_EDIT').subscribe(res => this.modalTitle = res);
toPromise<Endpoint>(this.endpointService
.getEndpoint(targetId))
.then(
target => {
this.target = target;
//Keep data cache
this.initVal = clone(target);
this.initVal.password = FAKE_PASSWORD;
this.target.password = FAKE_PASSWORD;
//Open the modal now
this.open();
this.forceRefreshView(1000);
})
.catch(error => this.errorHandler.error(error));
} else {
this.actionType = ActionType.ADD_NEW;
this.translateService.get('DESTINATION.TITLE_ADD').subscribe(res => this.modalTitle = res);
//Directly open the modal
this.open();
}
}
testConnection() {
let payload: Endpoint = this.initEndpoint();
if (this.endpointHasChanged) {
payload.endpoint = this.target.endpoint;
payload.username = this.target.username;
payload.password = this.target.password;
} else {
payload.id = this.target.id;
}
this.testOngoing = true;
toPromise<Endpoint>(this.endpointService
.pingEndpoint(payload))
.then(
response => {
this.inlineAlert.showInlineSuccess({ message: "DESTINATION.TEST_CONNECTION_SUCCESS" });
this.forceRefreshView(1000);
this.testOngoing = false;
}).catch(
error => {
this.inlineAlert.showInlineError('DESTINATION.TEST_CONNECTION_FAILURE');
this.forceRefreshView(1000);
this.testOngoing = false;
});
}
changedTargetName($event: any) {
if (this.editable) {
this.targetNameHasChanged = true;
}
}
clearPassword($event: any) {
if (this.editable) {
this.target.password = '';
this.endpointHasChanged = true;
}
}
onSubmit() {
switch (this.actionType) {
case ActionType.ADD_NEW:
this.addEndpoint();
break;
case ActionType.EDIT:
this.updateEndpoint();
break;
}
}
addEndpoint() {
if (this.onGoing) {
return;//Avoid duplicated submitting
}
this.onGoing = true;
toPromise<number>(this.endpointService
.createEndpoint(this.target))
.then(response => {
this.translateService.get('DESTINATION.CREATED_SUCCESS')
.subscribe(res => this.errorHandler.info(res));
this.reload.emit(true);
this.onGoing = false;
this.close();
}).catch(error => {
let errorMessageKey = this.handleErrorMessageKey(error.status);
this.translateService
.get(errorMessageKey)
.subscribe(res => {
this.inlineAlert.showInlineError(res);
this.onGoing = false;
});
}
);
}
updateEndpoint() {
if (this.onGoing) {
return;//Avoid duplicated submitting
}
if (!(this.targetNameHasChanged || this.endpointHasChanged)) {
return;//Avoid invalid submitting
}
let payload: Endpoint = this.initEndpoint();
if (this.targetNameHasChanged) {
payload.name = this.target.name;
delete payload.endpoint;
}
if (this.endpointHasChanged) {
payload.endpoint = this.target.endpoint;
payload.username = this.target.username;
payload.password = this.target.password;
delete payload.name;
}
if (!this.target.id) { return; }
this.onGoing = true;
toPromise<number>(this.endpointService
.updateEndpoint(this.target.id, payload))
.then(
response => {
this.translateService.get('DESTINATION.UPDATED_SUCCESS')
.subscribe(res => this.errorHandler.info(res));
this.reload.emit(true);
this.close();
this.onGoing = false;
})
.catch(
error => {
let errorMessageKey = this.handleErrorMessageKey(error.status);
this.translateService
.get(errorMessageKey)
.subscribe(res => {
this.inlineAlert.showInlineError(res);
});
this.onGoing = false;
}
);
}
handleErrorMessageKey(status: number): string {
switch (status) {
case 409: this
return 'DESTINATION.CONFLICT_NAME';
case 400:
return 'DESTINATION.INVALID_NAME';
default:
return 'UNKNOWN_ERROR';
}
}
onCancel() {
if (this.hasChanged) {
this.inlineAlert.showInlineConfirmation({ message: 'ALERT.FORM_CHANGE_CONFIRMATION' });
} else {
this.close();
if (this.targetForm) {
this.targetForm.reset();
}
}
}
confirmCancel(confirmed: boolean) {
this.inlineAlert.close();
this.close();
}
ngAfterViewChecked(): void {
if (this.targetForm != this.currentForm) {
this.targetForm = this.currentForm;
if (this.targetForm) {
this.valueChangesSub = this.targetForm.valueChanges.subscribe((data: { [key: string]: string } | any) => {
if (data) {
//To avoid invalid change publish events
let keyNumber: number = 0;
for (let key in data) {
//Empty string "" is accepted
if (data[key] !== null) {
keyNumber++;
}
}
if (keyNumber !== 4) {
return;
}
if (!compareValue(this.formValues, data)) {
this.formValues = data;
this.inlineAlert.close();
}
}
});
}
}
}
}
}

View File

@ -37,153 +37,167 @@ import { toPromise, CustomComparator } from '../utils';
import { State, Comparator } from 'clarity-angular';
@Component({
selector: 'hbr-endpoint',
template: ENDPOINT_TEMPLATE,
styles: [ ENDPOINT_STYLE ],
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'hbr-endpoint',
template: ENDPOINT_TEMPLATE,
styles: [ENDPOINT_STYLE],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class EndpointComponent implements OnInit {
@ViewChild(CreateEditEndpointComponent)
createEditEndpointComponent: CreateEditEndpointComponent;
@ViewChild(CreateEditEndpointComponent)
createEditEndpointComponent: CreateEditEndpointComponent;
@ViewChild('confirmationDialog')
confirmationDialogComponent: ConfirmationDialogComponent;
@ViewChild('confirmationDialog')
confirmationDialogComponent: ConfirmationDialogComponent;
targets: Endpoint[];
target: Endpoint;
targets: Endpoint[];
target: Endpoint;
targetName: string;
subscription: Subscription;
targetName: string;
subscription: Subscription;
loading: boolean = false;
loading: boolean = false;
creationTimeComparator: Comparator<Endpoint> = new CustomComparator<Endpoint>('creation_time', 'date');
creationTimeComparator: Comparator<Endpoint> = new CustomComparator<Endpoint>('creation_time', 'date');
get initEndpoint(): Endpoint {
return {
endpoint: "",
name: "",
username: "",
password: "",
type: 0
};
}
timerHandler: any;
constructor(
private endpointService: EndpointService,
private errorHandler: ErrorHandler,
private translateService: TranslateService,
private ref: ChangeDetectorRef) {
let hnd = setInterval(()=>ref.markForCheck(), 100);
setTimeout(()=>clearInterval(hnd), 1000);
}
get initEndpoint(): Endpoint {
return {
endpoint: "",
name: "",
username: "",
password: "",
type: 0
};
}
confirmDeletion(message: ConfirmationAcknowledgement) {
if (message &&
message.source === ConfirmationTargets.TARGET &&
message.state === ConfirmationState.CONFIRMED) {
let targetId = message.data;
toPromise<number>(this.endpointService
.deleteEndpoint(targetId))
.then(
response => {
this.translateService.get('DESTINATION.DELETED_SUCCESS')
.subscribe(res=>this.errorHandler.info(res));
this.reload(true);
}).catch(
error => {
if(error && error.status === 412) {
this.translateService.get('DESTINATION.FAILED_TO_DELETE_TARGET_IN_USED')
.subscribe(res=>this.errorHandler.error(res));
} else {
this.errorHandler.error(error);
constructor(
private endpointService: EndpointService,
private errorHandler: ErrorHandler,
private translateService: TranslateService,
private ref: ChangeDetectorRef) {
this.forceRefreshView(1000);
}
confirmDeletion(message: ConfirmationAcknowledgement) {
if (message &&
message.source === ConfirmationTargets.TARGET &&
message.state === ConfirmationState.CONFIRMED) {
let targetId = message.data;
toPromise<number>(this.endpointService
.deleteEndpoint(targetId))
.then(
response => {
this.translateService.get('DESTINATION.DELETED_SUCCESS')
.subscribe(res => this.errorHandler.info(res));
this.reload(true);
}).catch(
error => {
if (error && error.status === 412) {
this.translateService.get('DESTINATION.FAILED_TO_DELETE_TARGET_IN_USED')
.subscribe(res => this.errorHandler.error(res));
} else {
this.errorHandler.error(error);
}
});
}
}
ngOnInit(): void {
this.targetName = '';
this.retrieve();
}
ngOnDestroy(): void {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
retrieve(): void {
this.loading = true;
toPromise<Endpoint[]>(this.endpointService
.getEndpoints(this.targetName))
.then(
targets => {
this.targets = targets || [];
this.forceRefreshView(1000);
this.loading = false;
}).catch(error => {
this.errorHandler.error(error);
this.loading = false;
});
}
doSearchTargets(targetName: string) {
this.targetName = targetName;
this.retrieve();
}
refreshTargets() {
this.retrieve();
}
reload($event: any) {
this.targetName = '';
this.retrieve();
}
openModal() {
this.createEditEndpointComponent.openCreateEditTarget(true);
this.target = this.initEndpoint;
}
editTarget(target: Endpoint) {
if (target) {
let editable = true;
if (!target.id) {
return;
}
});
let id: number | string = target.id;
toPromise<ReplicationRule[]>(this.endpointService
.getEndpointWithReplicationRules(id))
.then(
rules => {
if (rules && rules.length > 0) {
rules.forEach((rule) => editable = (rule && rule.enabled !== 1));
}
this.createEditEndpointComponent.openCreateEditTarget(editable, id);
this.forceRefreshView(1000);
})
.catch(error => this.errorHandler.error(error));
}
}
}
ngOnInit(): void {
this.targetName = '';
this.retrieve();
}
ngOnDestroy(): void {
if (this.subscription) {
this.subscription.unsubscribe();
deleteTarget(target: Endpoint) {
if (target) {
let targetId = target.id;
let deletionMessage = new ConfirmationMessage(
'REPLICATION.DELETION_TITLE_TARGET',
'REPLICATION.DELETION_SUMMARY_TARGET',
target.name || '',
target.id,
ConfirmationTargets.TARGET,
ConfirmationButtons.DELETE_CANCEL);
this.confirmationDialogComponent.open(deletionMessage);
}
}
}
retrieve(): void {
this.loading = true;
toPromise<Endpoint[]>(this.endpointService
.getEndpoints(this.targetName))
.then(
targets => {
this.targets = targets || [];
let hnd = setInterval(()=>this.ref.markForCheck(), 100);
setTimeout(()=>clearInterval(hnd), 1000);
this.loading = false;
}).catch(error => {
this.errorHandler.error(error);
this.loading = false;
});
}
doSearchTargets(targetName: string) {
this.targetName = targetName;
this.retrieve();
}
refreshTargets() {
this.retrieve();
}
reload($event: any) {
this.targetName = '';
this.retrieve();
}
openModal() {
this.createEditEndpointComponent.openCreateEditTarget(true);
this.target = this.initEndpoint;
}
editTarget(target: Endpoint) {
if(target) {
let editable = true;
if (!target.id) {
return;
}
let id: number | string = target.id;
toPromise<ReplicationRule[]>(this.endpointService
.getEndpointWithReplicationRules(id))
.then(
rules=>{
if(rules && rules.length > 0) {
rules.forEach((rule)=>editable = (rule && rule.enabled !== 1));
}
this.createEditEndpointComponent.openCreateEditTarget(editable, id);
let hnd = setInterval(()=>this.ref.markForCheck(), 100);
setTimeout(()=>clearInterval(hnd), 1000);
})
.catch(error=>this.errorHandler.error(error));
//Forcely refresh the view
forceRefreshView(duration: number): void {
//Reset timer
if (this.timerHandler) {
clearInterval(this.timerHandler);
}
this.timerHandler = setInterval(() => this.ref.markForCheck(), 100);
setTimeout(() => {
if (this.timerHandler) {
clearInterval(this.timerHandler);
this.timerHandler = null;
}
}, duration);
}
}
deleteTarget(target: Endpoint) {
if (target) {
let targetId = target.id;
let deletionMessage = new ConfirmationMessage(
'REPLICATION.DELETION_TITLE_TARGET',
'REPLICATION.DELETION_SUMMARY_TARGET',
target.name || '',
target.id,
ConfirmationTargets.TARGET,
ConfirmationButtons.DELETE_CANCEL);
this.confirmationDialogComponent.open(deletionMessage);
}
}
}

View File

@ -48,8 +48,8 @@ export const TAG_DETAIL_HTML: string = `
</div>
</div>
<div class="second-column">
<div>{{highCount}} {{getPackageText(highCount) | translate}} {{'VULNERABILITY.SEVERITY.HIGH' | translate }} {{suffixForHigh | translate }}</div>
<div class="second-row">{{mediumCount}} {{getPackageText(mediumCount) | translate}} {{'VULNERABILITY.SEVERITY.MEDIUM' | translate }} {{suffixForMedium | translate }}</div>
<div>{{highCount}} {{'VULNERABILITY.SEVERITY.HIGH' | translate }}</div>
<div class="second-row">{{mediumCount}} {{'VULNERABILITY.SEVERITY.MEDIUM' | translate }}</div>
</div>
<div class="third-column">
<div>
@ -60,8 +60,8 @@ export const TAG_DETAIL_HTML: string = `
</div>
</div>
<div class="fourth-column">
<div>{{lowCount}} {{getPackageText(lowCount) | translate}} {{'VULNERABILITY.SEVERITY.LOW' | translate }} {{suffixForLow | translate }}</div>
<div class="second-row">{{unknownCount}} {{getPackageText(unknownCount) | translate}} {{'VULNERABILITY.SEVERITY.UNKNOWN' | translate }} {{suffixForUnknown | translate }}</div>
<div>{{lowCount}} {{'VULNERABILITY.SEVERITY.LOW' | translate }}</div>
<div class="second-row">{{unknownCount}} {{'VULNERABILITY.SEVERITY.UNKNOWN' | translate }}</div>
</div>
</div>
</div>

View File

@ -144,7 +144,7 @@ describe('TagDetailComponent (inline template)', () => {
expect(el).toBeTruthy();
let el2: HTMLElement = el.querySelector('div');
expect(el2).toBeTruthy();
expect(el2.textContent.trim()).toEqual("13 VULNERABILITY.PACKAGES VULNERABILITY.SEVERITY.HIGH VULNERABILITY.PLURAL");
expect(el2.textContent.trim()).toEqual("13 VULNERABILITY.SEVERITY.HIGH");
});
}));

View File

@ -155,7 +155,7 @@ export function calculatePage(state: State): number {
* @param {State} state
* @returns {void}
*/
export function doFiltering<T extends {[key:string]: any | any[]}>(items: T[], state: State): T[] {
export function doFiltering<T extends { [key: string]: any | any[] }>(items: T[], state: State): T[] {
if (!items || items.length === 0) {
return items;
}
@ -187,7 +187,16 @@ export function regexpFilter(terms: string, testedValue: any): boolean {
return reg.test(testedValue);
}
export function doSorting<T extends {[key:string]: any | any[]}>(items: T[], state: State): T[] {
/**
* Sorting the data by column
*
* @export
* @template T
* @param {T[]} items
* @param {State} state
* @returns {T[]}
*/
export function doSorting<T extends { [key: string]: any | any[] }>(items: T[], state: State): T[] {
if (!items || items.length === 0) {
return items;
}
@ -218,4 +227,42 @@ export function doSorting<T extends {[key:string]: any | any[]}>(items: T[], sta
return comp;
});
}
/**
* Compare the two objects to adjust if they're equal
*
* @export
* @param {*} a
* @param {*} b
* @returns {boolean}
*/
export function compareValue(a: any, b: any): boolean {
if ((a && !b) || (!a && b)) return false;
if (!a && !b) return true;
return JSON.stringify(a) === JSON.stringify(b);
}
/**
* Check if the object is null or empty '{}'
*
* @export
* @param {*} obj
* @returns {boolean}
*/
export function isEmptyObject(obj: any): boolean {
return !obj || JSON.stringify(obj) === "{}";
}
/**
* Deeper clone all
*
* @export
* @param {*} srcObj
* @returns {*}
*/
export function clone(srcObj: any): any {
if (!srcObj) return null;
return JSON.parse(JSON.stringify(srcObj));
}

View File

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

View File

@ -497,8 +497,8 @@
"UNKNOWN": "unknown",
"NONE": "none"
},
"SINGULAR": "Vulnerability",
"PLURAL": "Vulnerabilities",
"SINGULAR": "vulnerability",
"PLURAL": "vulnerabilities",
"PLACEHOLDER": "Filter Vulnerabilities",
"PACKAGE": "package",
"PACKAGES": "packages",

View File

@ -496,8 +496,8 @@
"UNKNOWN": "unknown",
"NONE": "none"
},
"SINGULAR": "Vulnerability",
"PLURAL": "Vulnerabilities",
"SINGULAR": "vulnerability",
"PLURAL": "vulnerabilities",
"PLACEHOLDER": "Filter Vulnerabilities",
"PACKAGE": "package",
"PACKAGES": "packages",