From 5071dcf304c86761ebd38da2fd38eba133d3315c Mon Sep 17 00:00:00 2001 From: kunw Date: Thu, 11 May 2017 14:59:12 +0800 Subject: [PATCH] Add shareable endpoint components. --- .../confirmation-dialog.component.css.ts | 21 ++ .../confirmation-dialog.component.html.ts | 28 ++ .../confirmation-dialog.component.ts | 90 ++++++ .../confirmation-message.ts | 31 ++ .../confirmation-state-message.ts | 26 ++ .../lib/src/confirmation-dialog/index.ts | 7 + .../create-edit-endpoint.component.css.ts | 6 + .../create-edit-endpoint.component.html.ts | 53 +++ .../create-edit-endpoint.component.spec.ts | 96 ++++++ .../create-edit-endpoint.component.ts | 301 ++++++++++++++++++ .../lib/src/create-edit-endpoint/index.ts | 7 + .../src/endpoint/endpoint.component.css.ts | 10 + .../src/endpoint/endpoint.component.html.ts | 36 +++ .../src/endpoint/endpoint.component.spec.ts | 133 ++++++++ .../lib/src/endpoint/endpoint.component.ts | 181 +++++++++++ src/ui_ng/lib/src/endpoint/index.ts | 7 + src/ui_ng/lib/src/harbor-library.module.ts | 24 +- src/ui_ng/lib/src/index.ts | 3 +- src/ui_ng/lib/src/inline-alert/index.ts | 7 + .../inline-alert.component.css.ts | 10 + .../inline-alert.component.html.ts | 13 + .../inline-alert/inline-alert.component.ts | 99 ++++++ .../lib/src/service/endpoint.service.spec.ts | 5 +- src/ui_ng/lib/src/service/endpoint.service.ts | 69 +++- src/ui_ng/lib/src/service/interface.ts | 8 +- .../lib/src/service/replication.service.ts | 1 - src/ui_ng/lib/src/shared/shared.const.ts | 68 ++++ src/ui_ng/lib/src/shared/shared.module.ts | 4 +- src/ui_ng/lib/src/shared/shared.utils.ts | 49 +++ 29 files changed, 1375 insertions(+), 18 deletions(-) create mode 100644 src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.css.ts create mode 100644 src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.html.ts create mode 100644 src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.ts create mode 100644 src/ui_ng/lib/src/confirmation-dialog/confirmation-message.ts create mode 100644 src/ui_ng/lib/src/confirmation-dialog/confirmation-state-message.ts create mode 100644 src/ui_ng/lib/src/confirmation-dialog/index.ts create mode 100644 src/ui_ng/lib/src/create-edit-endpoint/create-edit-endpoint.component.css.ts create mode 100644 src/ui_ng/lib/src/create-edit-endpoint/create-edit-endpoint.component.html.ts create mode 100644 src/ui_ng/lib/src/create-edit-endpoint/create-edit-endpoint.component.spec.ts create mode 100644 src/ui_ng/lib/src/create-edit-endpoint/create-edit-endpoint.component.ts create mode 100644 src/ui_ng/lib/src/create-edit-endpoint/index.ts create mode 100644 src/ui_ng/lib/src/endpoint/endpoint.component.css.ts create mode 100644 src/ui_ng/lib/src/endpoint/endpoint.component.html.ts create mode 100644 src/ui_ng/lib/src/endpoint/endpoint.component.spec.ts create mode 100644 src/ui_ng/lib/src/endpoint/endpoint.component.ts create mode 100644 src/ui_ng/lib/src/endpoint/index.ts create mode 100644 src/ui_ng/lib/src/inline-alert/index.ts create mode 100644 src/ui_ng/lib/src/inline-alert/inline-alert.component.css.ts create mode 100644 src/ui_ng/lib/src/inline-alert/inline-alert.component.html.ts create mode 100644 src/ui_ng/lib/src/inline-alert/inline-alert.component.ts create mode 100644 src/ui_ng/lib/src/shared/shared.const.ts create mode 100644 src/ui_ng/lib/src/shared/shared.utils.ts diff --git a/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.css.ts b/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.css.ts new file mode 100644 index 000000000..0a156cdb7 --- /dev/null +++ b/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.css.ts @@ -0,0 +1,21 @@ +export const CONFIRMATION_DIALOG_STYLE: string = ` +.confirmation-icon-inline { + display: inline-block; +} + +.confirmation-title { + line-height: 24px; + color: #000000; + font-size: 22px; +} + +.confirmation-content { + font-size: 14px; + color: #565656; + line-height: 24px; + display: inline-block; + vertical-align: middle; + width: 80%; + white-space: pre-wrap; +} +`; \ No newline at end of file diff --git a/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.html.ts b/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.html.ts new file mode 100644 index 000000000..15797278b --- /dev/null +++ b/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.html.ts @@ -0,0 +1,28 @@ +export const CONFIRMATION_DIALOG_TEMPLATE: string = ` + + + + + +`; \ No newline at end of file diff --git a/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.ts b/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.ts new file mode 100644 index 000000000..efda5ce13 --- /dev/null +++ b/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.ts @@ -0,0 +1,90 @@ +// Copyright (c) 2017 VMware, Inc. 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. +import { Component, EventEmitter, Output } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; + +import { ConfirmationMessage } from './confirmation-message'; +import { ConfirmationAcknowledgement } from './confirmation-state-message'; +import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const'; + +import { CONFIRMATION_DIALOG_TEMPLATE } from './confirmation-dialog.component.html'; +import { CONFIRMATION_DIALOG_STYLE } from './confirmation-dialog.component.css'; + +@Component({ + selector: 'confirmation-dialog', + template: CONFIRMATION_DIALOG_TEMPLATE, + styles: [ CONFIRMATION_DIALOG_STYLE ] +}) + +export class ConfirmationDialogComponent { + opened: boolean = false; + dialogTitle: string = ""; + dialogContent: string = ""; + message: ConfirmationMessage; + buttons: ConfirmationButtons; + + @Output() confirmAction = new EventEmitter(); + @Output() cancelAction = new EventEmitter(); + + constructor( + private translate: TranslateService) {} + + open(msg: ConfirmationMessage): void { + this.dialogTitle = msg.title; + this.dialogContent = msg.message; + this.message = msg; + this.translate.get(this.dialogTitle).subscribe((res: string) => this.dialogTitle = res); + this.translate.get(this.dialogContent, { 'param': msg.param }).subscribe((res: string) => this.dialogContent = res); + //Open dialog + this.buttons = msg.buttons; + this.opened = true; + } + + close(): void { + this.opened = false; + } + + cancel(): void { + if(!this.message){//Inproper condition + this.close(); + return; + } + + let data: any = this.message.data ? this.message.data : {}; + let target = this.message.targetId ? this.message.targetId : ConfirmationTargets.EMPTY; + this.cancelAction.emit(new ConfirmationAcknowledgement( + ConfirmationState.CANCEL, + data, + target + )); + this.close(); + } + + confirm(): void { + if(!this.message){//Inproper condition + this.close(); + return; + } + + let data: any = this.message.data ? this.message.data : {}; + let target = this.message.targetId ? this.message.targetId : ConfirmationTargets.EMPTY; + let message = new ConfirmationAcknowledgement( + ConfirmationState.CONFIRMED, + data, + target + ); + this.confirmAction.emit(message); + this.close(); + } +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/confirmation-dialog/confirmation-message.ts b/src/ui_ng/lib/src/confirmation-dialog/confirmation-message.ts new file mode 100644 index 000000000..a06f4fecd --- /dev/null +++ b/src/ui_ng/lib/src/confirmation-dialog/confirmation-message.ts @@ -0,0 +1,31 @@ +// Copyright (c) 2017 VMware, Inc. 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. +import { ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const'; + +export class ConfirmationMessage { + public constructor(title: string, message: string, param: string, data: any, targetId: ConfirmationTargets, buttons?: ConfirmationButtons) { + this.title = title; + this.message = message; + this.data = data; + this.targetId = targetId; + this.param = param; + this.buttons = buttons ? buttons : ConfirmationButtons.CONFIRM_CANCEL; + } + title: string; + message: string; + data: any = {};//default is empty + targetId: ConfirmationTargets = ConfirmationTargets.EMPTY; + param: string; + buttons: ConfirmationButtons; +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/confirmation-dialog/confirmation-state-message.ts b/src/ui_ng/lib/src/confirmation-dialog/confirmation-state-message.ts new file mode 100644 index 000000000..19c15ff4c --- /dev/null +++ b/src/ui_ng/lib/src/confirmation-dialog/confirmation-state-message.ts @@ -0,0 +1,26 @@ +// Copyright (c) 2017 VMware, Inc. 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. +import { ConfirmationState, ConfirmationTargets } from '../shared/shared.const'; + +export class ConfirmationAcknowledgement { + constructor(state: ConfirmationState, data: any, source: ConfirmationTargets) { + this.state = state; + this.data = data; + this.source = source; + } + + state: ConfirmationState = ConfirmationState.NA; + data: any = {}; + source: ConfirmationTargets = ConfirmationTargets.EMPTY; +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/confirmation-dialog/index.ts b/src/ui_ng/lib/src/confirmation-dialog/index.ts new file mode 100644 index 000000000..7d1025cef --- /dev/null +++ b/src/ui_ng/lib/src/confirmation-dialog/index.ts @@ -0,0 +1,7 @@ +import { Type } from '@angular/core'; + +import { ConfirmationDialogComponent } from './confirmation-dialog.component'; + +export const CONFIRMATION_DIALOG_DIRECTIVES: Type[] = [ + ConfirmationDialogComponent +]; \ No newline at end of file diff --git a/src/ui_ng/lib/src/create-edit-endpoint/create-edit-endpoint.component.css.ts b/src/ui_ng/lib/src/create-edit-endpoint/create-edit-endpoint.component.css.ts new file mode 100644 index 000000000..4ebc9680a --- /dev/null +++ b/src/ui_ng/lib/src/create-edit-endpoint/create-edit-endpoint.component.css.ts @@ -0,0 +1,6 @@ +export const CREATE_EDIT_ENDPOINT_STYLE: string = ` + .form-group-label-override { + font-size: 14px; + font-weight: 400; + } +`; \ No newline at end of file diff --git a/src/ui_ng/lib/src/create-edit-endpoint/create-edit-endpoint.component.html.ts b/src/ui_ng/lib/src/create-edit-endpoint/create-edit-endpoint.component.html.ts new file mode 100644 index 000000000..5ef882d06 --- /dev/null +++ b/src/ui_ng/lib/src/create-edit-endpoint/create-edit-endpoint.component.html.ts @@ -0,0 +1,53 @@ +export const CREATE_EDIT_ENDPOINT_TEMPLATE: string = ` + + + + +`; \ No newline at end of file diff --git a/src/ui_ng/lib/src/create-edit-endpoint/create-edit-endpoint.component.spec.ts b/src/ui_ng/lib/src/create-edit-endpoint/create-edit-endpoint.component.spec.ts new file mode 100644 index 000000000..c39f19cf9 --- /dev/null +++ b/src/ui_ng/lib/src/create-edit-endpoint/create-edit-endpoint.component.spec.ts @@ -0,0 +1,96 @@ +import { ComponentFixture, TestBed, async, fakeAsync, tick } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; + +import { TranslateService } from '@ngx-translate/core'; + +import { SharedModule } from '../shared/shared.module'; + +import { FilterComponent } from '../filter/filter.component'; + +import { CreateEditEndpointComponent } from '../create-edit-endpoint/create-edit-endpoint.component'; +import { InlineAlertComponent } from '../inline-alert/inline-alert.component'; +import { ErrorHandler } from '../error-handler/error-handler'; +import { Endpoint } from '../service/interface'; +import { EndpointService, EndpointDefaultService } from '../service/endpoint.service'; +import { IServiceConfig, SERVICE_CONFIG } from '../service.config'; +describe('CreateEditEndpointComponent (inline template)', () => { + + let mockData: Endpoint = { + "id": 1, + "endpoint": "https://10.117.4.151", + "name": "target_01", + "username": "admin", + "password": "", + "type": 0 + }; + + let comp: CreateEditEndpointComponent; + let fixture: ComponentFixture; + let de: DebugElement; + let el: HTMLElement; + + let config: IServiceConfig = { + systemInfoEndpoint: '/api/endpoints/testing' + }; + + let endpointService: EndpointService; + + let spy: jasmine.Spy; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ SharedModule ], + declarations: [ + FilterComponent, + CreateEditEndpointComponent, + InlineAlertComponent ], + providers: [ + ErrorHandler, + { provide: SERVICE_CONFIG, useValue: config }, + { provide: EndpointService, useClass: EndpointDefaultService }, + { provide: TranslateService, useClass: TranslateService} + ] + }); + })); + + beforeEach(()=>{ + fixture = TestBed.createComponent(CreateEditEndpointComponent); + comp = fixture.componentInstance; + + endpointService = fixture.debugElement.injector.get(EndpointService); + spy = spyOn(endpointService, 'getEndpoint').and.returnValue(Promise.resolve(mockData)); + fixture.detectChanges(); + }); + + it('should be created', () => { + fixture.detectChanges(); + expect(comp).toBeTruthy(); + }); + + it('should get endpoint be called', async(()=>{ + fixture.detectChanges(); + comp.openCreateEditTarget(true, 1); + comp.createEditDestinationOpened = false; + fixture.whenStable().then(()=>{ + fixture.detectChanges(); + expect(spy.calls.any()).toBeTruthy(); + }); + })); + + it('should get endpoint to open modal', async(()=>{ + fixture.detectChanges(); + comp.openCreateEditTarget(true, 1); + comp.createEditDestinationOpened = false; + fixture.whenStable().then(()=>{ + fixture.detectChanges(); + expect(comp.target.name).toEqual('target_01'); + }); + })); + + it('should endpoint be initialized', () => { + fixture.detectChanges(); + expect(config.systemInfoEndpoint).toEqual('/api/endpoints/testing'); + }); + +}); \ No newline at end of file diff --git a/src/ui_ng/lib/src/create-edit-endpoint/create-edit-endpoint.component.ts b/src/ui_ng/lib/src/create-edit-endpoint/create-edit-endpoint.component.ts new file mode 100644 index 000000000..c598c3183 --- /dev/null +++ b/src/ui_ng/lib/src/create-edit-endpoint/create-edit-endpoint.component.ts @@ -0,0 +1,301 @@ +// Copyright (c) 2017 VMware, Inc. 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. +import { Component, Output, EventEmitter, ViewChild, AfterViewChecked } from '@angular/core'; +import { NgForm } from '@angular/forms'; + +import { EndpointService } from '../service/endpoint.service'; +import { ErrorHandler } from '../error-handler/index'; +import { ActionType } from '../shared/shared.const'; + +import { InlineAlertComponent } from '../inline-alert/inline-alert.component'; + +import { Endpoint } from '../service/interface'; + +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 } from '../utils'; + +const FAKE_PASSWORD = 'rjGcfuRu'; + +@Component({ + selector: 'create-edit-endpoint', + template: CREATE_EDIT_ENDPOINT_TEMPLATE, + styles: [ CREATE_EDIT_ENDPOINT_STYLE ] +}) +export class CreateEditEndpointComponent implements AfterViewChecked { + + modalTitle: string; + createEditDestinationOpened: boolean; + + editable: boolean; + + testOngoing: boolean; + pingTestMessage: string; + pingStatus: boolean; + + actionType: ActionType; + + target: Endpoint = Object.assign({}, this.initEndpoint); + initVal: Endpoint = Object.assign({}, this.initEndpoint); + + targetForm: NgForm; + + staticBackdrop: boolean = true; + closable: boolean = false; + + @ViewChild('targetForm') + currentForm: NgForm; + + hasChanged: boolean; + + endpointHasChanged: boolean; + targetNameHasChanged: boolean; + + @ViewChild(InlineAlertComponent) + inlineAlert: InlineAlertComponent; + + @Output() reload = new EventEmitter(); + + + 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) { + + this.target = Object.assign({}, this.initEndpoint); + this.editable = editable; + this.createEditDestinationOpened = true; + this.hasChanged = false; + this.endpointHasChanged = false; + this.targetNameHasChanged = false; + + this.pingTestMessage = ''; + this.pingStatus = true; + this.testOngoing = false; + + if(targetId) { + this.actionType = ActionType.EDIT; + this.translateService.get('DESTINATION.TITLE_EDIT').subscribe(res=>this.modalTitle=res); + toPromise(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() { + this.translateService.get('DESTINATION.TESTING_CONNECTION').subscribe(res=>this.pingTestMessage=res); + this.pingStatus = true; + this.testOngoing = !this.testOngoing; + + let payload: Endpoint = Object.assign({}, 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; + } + + toPromise(this.endpointService + .pingEndpoint(payload)) + .then( + response=>{ + this.pingStatus = true; + this.translateService.get('DESTINATION.TEST_CONNECTION_SUCCESS').subscribe(res=>this.pingTestMessage=res); + this.testOngoing = !this.testOngoing; + }).catch( + error=>{ + this.pingStatus = false; + this.translateService.get('DESTINATION.TEST_CONNECTION_FAILURE').subscribe(res=>this.pingTestMessage=res); + this.testOngoing = !this.testOngoing; + + }); + } + + 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: + toPromise(this.endpointService + .createEndpoint(this.target)) + .then( + response=>{ + this.errorHandler.info('DESTINATION.CREATED_SUCCESS'); + this.createEditDestinationOpened = false; + this.reload.emit(true); + }) + .catch( + error=>{ + let errorMessageKey = ''; + switch(error.status) { + case 409: + errorMessageKey = 'DESTINATION.CONFLICT_NAME'; + break; + case 400: + errorMessageKey = 'DESTINATION.INVALID_NAME'; + break; + default: + errorMessageKey = 'UNKNOWN_ERROR'; + } + + this.translateService + .get(errorMessageKey) + .subscribe(res=>{ + // if(this.messageHandlerService.isAppLevel(error)) { + // this.messageHandlerService.handleError(error); + // this.createEditDestinationOpened = false; + // } else { + // this.inlineAlert.showInlineError(res); + // } + this.errorHandler.error(res); + }); + } + ); + break; + case ActionType.EDIT: + if(!(this.targetNameHasChanged || this.endpointHasChanged)) { + this.createEditDestinationOpened = false; + return; + } + let payload: Endpoint = Object.assign({}, this.initEndpoint); + if(this.targetNameHasChanged) { + payload.name = this.target.name; + } + if (this.endpointHasChanged) { + payload.endpoint = this.target.endpoint; + payload.username = this.target.username; + payload.password = this.target.password; + delete payload.name; + } + toPromise(this.endpointService + .updateEndpoint(this.target.id, payload)) + .then( + response=>{ + this.errorHandler.info('DESTINATION.UPDATED_SUCCESS'); + this.createEditDestinationOpened = false; + this.reload.emit(true); + }) + .catch( + error=>{ + let errorMessageKey = ''; + switch(error.status) { + case 409:this + errorMessageKey = 'DESTINATION.CONFLICT_NAME'; + break; + case 400: + errorMessageKey = 'DESTINATION.INVALID_NAME'; + break; + default: + errorMessageKey = 'UNKNOWN_ERROR'; + } + this.translateService + .get(errorMessageKey) + .subscribe(res=>{ + // if(this.messageHandlerService.isAppLevel(error)) { + // this.messageHandlerService.handleError(error); + // this.createEditDestinationOpened = false; + // } else { + // this.inlineAlert.showInlineError(res); + // } + this.errorHandler.error(res); + }); + } + ); + break; + } + } + + 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]: string} = { + targetName: this.initVal.name, + endpointUrl: this.initVal.endpoint, + username: this.initVal.username, + password: this.initVal.password + }; + this.targetForm.valueChanges.subscribe(data=>{ + 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(); + } + } + }); + } + } + +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/create-edit-endpoint/index.ts b/src/ui_ng/lib/src/create-edit-endpoint/index.ts new file mode 100644 index 000000000..be0ec4caa --- /dev/null +++ b/src/ui_ng/lib/src/create-edit-endpoint/index.ts @@ -0,0 +1,7 @@ +import { Type } from '@angular/core'; +import { CreateEditEndpointComponent } from './create-edit-endpoint.component'; + + +export const CREATE_EDIT_ENDPOINT_DIRECTIVES: Type[] = [ + CreateEditEndpointComponent +]; \ No newline at end of file diff --git a/src/ui_ng/lib/src/endpoint/endpoint.component.css.ts b/src/ui_ng/lib/src/endpoint/endpoint.component.css.ts new file mode 100644 index 000000000..8eab04166 --- /dev/null +++ b/src/ui_ng/lib/src/endpoint/endpoint.component.css.ts @@ -0,0 +1,10 @@ +export const ENDPOINT_STYLE: string = ` + .option-left { + padding-left: 16px; + margin-top: 24px; + } + .option-right { + padding-right: 16px; + margin-top: 36px; + } +`; \ No newline at end of file diff --git a/src/ui_ng/lib/src/endpoint/endpoint.component.html.ts b/src/ui_ng/lib/src/endpoint/endpoint.component.html.ts new file mode 100644 index 000000000..e1f74a117 --- /dev/null +++ b/src/ui_ng/lib/src/endpoint/endpoint.component.html.ts @@ -0,0 +1,36 @@ +export const ENDPOINT_TEMPLATE: string = ` + +
+
+
+
+ + +
+
+ + + + +
+
+
+
+ + {{'DESTINATION.NAME' | translate}} + {{'DESTINATION.URL' | translate}} + {{'DESTINATION.CREATION_TIME' | translate}} + + + + + + {{t.name}} + {{t.endpoint}} + {{t.creation_time | date: 'short'}} + + {{ (targets ? targets.length : 0) }} {{'DESTINATION.ITEMS' | translate}} + +
+
+`; \ No newline at end of file diff --git a/src/ui_ng/lib/src/endpoint/endpoint.component.spec.ts b/src/ui_ng/lib/src/endpoint/endpoint.component.spec.ts new file mode 100644 index 000000000..27be4ef9c --- /dev/null +++ b/src/ui_ng/lib/src/endpoint/endpoint.component.spec.ts @@ -0,0 +1,133 @@ +import { ComponentFixture, TestBed, async } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; + +import { SharedModule } from '../shared/shared.module'; +import { EndpointComponent } from './endpoint.component'; +import { FilterComponent } from '../filter/filter.component'; +import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component'; +import { CreateEditEndpointComponent } from '../create-edit-endpoint/create-edit-endpoint.component'; +import { InlineAlertComponent } from '../inline-alert/inline-alert.component'; +import { ErrorHandler } from '../error-handler/error-handler'; +import { Endpoint } from '../service/interface'; +import { EndpointService, EndpointDefaultService } from '../service/endpoint.service'; +import { IServiceConfig, SERVICE_CONFIG } from '../service.config'; +describe('EndpointComponent (inline template)', () => { + + let mockData: Endpoint[] = [ + { + "id": 1, + "endpoint": "https://10.117.4.151", + "name": "target_01", + "username": "admin", + "password": "", + "type": 0 + }, + { + "id": 2, + "endpoint": "https://10.117.5.142", + "name": "target_02", + "username": "AAA", + "password": "", + "type": 0 + }, + { + "id": 3, + "endpoint": "https://101.1.11.111", + "name": "target_03", + "username": "admin", + "password": "", + "type": 0 + }, + { + "id": 4, + "endpoint": "http://4.4.4.4", + "name": "target_04", + "username": "", + "password": "", + "type": 0 + } + ]; + + let mockOne: Endpoint = { + "id": 1, + "endpoint": "https://10.117.4.151", + "name": "target_01", + "username": "admin", + "password": "", + "type": 0 + }; + + let comp: EndpointComponent; + let fixture: ComponentFixture; + let de: DebugElement; + let el: HTMLElement; + + let config: IServiceConfig = { + systemInfoEndpoint: '/api/endpoints/testing' + }; + + let endpointService: EndpointService; + let spy: jasmine.Spy; + let spyOnRules: jasmine.Spy; + let spyOne: jasmine.Spy; + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ SharedModule ], + declarations: [ + FilterComponent, + ConfirmationDialogComponent, + CreateEditEndpointComponent, + InlineAlertComponent, + EndpointComponent ], + providers: [ + ErrorHandler, + { provide: SERVICE_CONFIG, useValue: config }, + { provide: EndpointService, useClass: EndpointDefaultService } + ] + }); + })); + + beforeEach(()=>{ + fixture = TestBed.createComponent(EndpointComponent); + comp = fixture.componentInstance; + + endpointService = fixture.debugElement.injector.get(EndpointService); + + spy = spyOn(endpointService, 'getEndpoints').and.returnValues(Promise.resolve(mockData)); + spyOnRules = spyOn(endpointService, 'getEndpointWithReplicationRules').and.returnValue([]); + spyOne = spyOn(endpointService, 'getEndpoint').and.returnValue(Promise.resolve(mockOne)); + fixture.detectChanges(); + }); + + it('should retrieve endpoint data', () => { + fixture.detectChanges(); + expect(spy.calls.any()).toBeTruthy(); + }); + + it('should endpoint be initialized', () => { + fixture.detectChanges(); + expect(config.systemInfoEndpoint).toEqual('/api/endpoints/testing'); + }); + + it('should open create endpoint modal', async(() => { + fixture.detectChanges(); + comp.editTarget(mockOne); + fixture.whenStable().then(()=>{ + fixture.detectChanges(); + expect(comp.target.name).toEqual('target_01'); + }); + })); + + it('should filter endpoints by keyword', async(() => { + fixture.detectChanges(); + + fixture.whenStable().then(()=>{ + fixture.detectChanges(); + comp.doSearchTargets('target_02'); + fixture.detectChanges(); + expect(comp.targets.length).toEqual(1); + }); + })); + +}); \ No newline at end of file diff --git a/src/ui_ng/lib/src/endpoint/endpoint.component.ts b/src/ui_ng/lib/src/endpoint/endpoint.component.ts new file mode 100644 index 000000000..b2555031d --- /dev/null +++ b/src/ui_ng/lib/src/endpoint/endpoint.component.ts @@ -0,0 +1,181 @@ +// Copyright (c) 2017 VMware, Inc. 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. +import { Component, OnInit, ViewChild, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; +import { Endpoint, ReplicationRule } from '../service/interface'; +import { EndpointService } from '../service/endpoint.service'; +import { ErrorHandler } from '../error-handler/index'; + +import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message'; +import { ConfirmationAcknowledgement } from '../confirmation-dialog/confirmation-state-message'; +import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component'; + +import { ConfirmationTargets, ConfirmationState, ConfirmationButtons } from '../shared/shared.const'; + +import { Subscription } from 'rxjs/Subscription'; + +import { CreateEditEndpointComponent } from '../create-edit-endpoint/create-edit-endpoint.component'; + +import { ENDPOINT_STYLE } from './endpoint.component.css'; +import { ENDPOINT_TEMPLATE } from './endpoint.component.html'; + +import { toPromise } from '../utils'; + +@Component({ + selector: 'hbr-endpoint', + template: ENDPOINT_TEMPLATE, + styles: [ ENDPOINT_STYLE ], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class EndpointComponent implements OnInit { + + @ViewChild(CreateEditEndpointComponent) + createEditEndpointComponent: CreateEditEndpointComponent; + + + @ViewChild('confirmationDialog') + confirmationDialogComponent: ConfirmationDialogComponent; + + targets: Endpoint[]; + target: Endpoint; + + targetName: string; + subscription: Subscription; + + get initEndpoint(): Endpoint { + return { + endpoint: "", + name: "", + username: "", + password: "", + type: 0 + }; + } + + constructor( + private endpointService: EndpointService, + private errorHandler: ErrorHandler, + private ref: ChangeDetectorRef) { + let hnd = setInterval(()=>ref.markForCheck(), 100); + setTimeout(()=>clearInterval(hnd), 1000); + } + + confirmDeletion(message: ConfirmationAcknowledgement) { + if (message && + message.source === ConfirmationTargets.TARGET && + message.state === ConfirmationState.CONFIRMED) { + + let targetId = message.data; + toPromise(this.endpointService + .deleteEndpoint(targetId)) + .then( + response => { + this.errorHandler.error('DESTINATION.DELETED_SUCCESS'); + this.reload(true); + }).catch( + error => { + if(error && error.status === 412) { + this.errorHandler.error('DESTINATION.FAILED_TO_DELETE_TARGET_IN_USED'); + } else { + this.errorHandler.error(error); + } + }); + } + } + + cancelDeletion(message: ConfirmationAcknowledgement) { + console.log('Received message from cancelAction:' + JSON.stringify(message)); + } + + + ngOnInit(): void { + this.targetName = ''; + this.retrieve(''); + } + + ngOnDestroy(): void { + if (this.subscription) { + this.subscription.unsubscribe(); + } + } + + retrieve(targetName: string): void { + toPromise(this.endpointService + .getEndpoints(targetName)) + .then( + targets => { + this.targets = targets || []; + let hnd = setInterval(()=>this.ref.markForCheck(), 100); + setTimeout(()=>clearInterval(hnd), 1000); + }).catch(error => this.errorHandler.error(error)); + } + + doSearchTargets(targetName: string) { + this.targetName = targetName; + this.retrieve(targetName); + } + + 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; + + toPromise(this.endpointService + .getEndpointWithReplicationRules(target.id)) + .then( + rules=>{ + if(rules && rules.length > 0) { + for(let i = 0; i < rules.length; i++){ + let p: ReplicationRule = rules[i]; + if(p.enabled === 1) { + editable = false; + break; + } + } + } + this.createEditEndpointComponent.openCreateEditTarget(editable, +target.id); + let hnd = setInterval(()=>this.ref.markForCheck(), 100); + setTimeout(()=>clearInterval(hnd), 1000); + }) + .catch(error=>this.errorHandler.error(error)); + } + } + + deleteTarget(target: Endpoint) { + console.log('Endpoint:' + JSON.stringify(target)); + 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); + } + } +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/endpoint/index.ts b/src/ui_ng/lib/src/endpoint/index.ts new file mode 100644 index 000000000..379a7f6f6 --- /dev/null +++ b/src/ui_ng/lib/src/endpoint/index.ts @@ -0,0 +1,7 @@ +import { Type } from '@angular/core'; +import { EndpointComponent } from './endpoint.component'; + + +export const ENDPOINT_DIRECTIVES: Type[] = [ + EndpointComponent +]; \ No newline at end of file diff --git a/src/ui_ng/lib/src/harbor-library.module.ts b/src/ui_ng/lib/src/harbor-library.module.ts index 618896b3c..30236cd7d 100644 --- a/src/ui_ng/lib/src/harbor-library.module.ts +++ b/src/ui_ng/lib/src/harbor-library.module.ts @@ -2,7 +2,14 @@ import { NgModule, ModuleWithProviders, Provider, APP_INITIALIZER, Inject } from import { LOG_DIRECTIVES } from './log/index'; import { FILTER_DIRECTIVES } from './filter/index'; +import { ENDPOINT_DIRECTIVES } from './endpoint/index'; +import { CREATE_EDIT_ENDPOINT_DIRECTIVES } from './create-edit-endpoint/index'; + import { SERVICE_CONFIG, IServiceConfig } from './service.config'; + +import { CONFIRMATION_DIALOG_DIRECTIVES } from './confirmation-dialog/index'; +import { INLINE_ALERT_DIRECTIVES } from './inline-alert/index'; + import { AccessLogService, AccessLogDefaultService, @@ -110,12 +117,21 @@ export function initConfig(translateService: TranslateService, config: IServiceC ], declarations: [ LOG_DIRECTIVES, - FILTER_DIRECTIVES + FILTER_DIRECTIVES, + ENDPOINT_DIRECTIVES, + CREATE_EDIT_ENDPOINT_DIRECTIVES, + CONFIRMATION_DIALOG_DIRECTIVES, + INLINE_ALERT_DIRECTIVES ], exports: [ LOG_DIRECTIVES, - FILTER_DIRECTIVES - ] + FILTER_DIRECTIVES, + ENDPOINT_DIRECTIVES, + CREATE_EDIT_ENDPOINT_DIRECTIVES, + CONFIRMATION_DIALOG_DIRECTIVES, + INLINE_ALERT_DIRECTIVES + ], + providers: [] }) export class HarborLibraryModule { @@ -156,4 +172,4 @@ export class HarborLibraryModule { ] }; } -} +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/index.ts b/src/ui_ng/lib/src/index.ts index c85fd63ba..5a0611ab2 100644 --- a/src/ui_ng/lib/src/index.ts +++ b/src/ui_ng/lib/src/index.ts @@ -4,4 +4,5 @@ export * from './service/index'; export * from './error-handler/index'; //export * from './utils'; export * from './log/index'; -export * from './filter/index'; \ No newline at end of file +export * from './filter/index'; +export * from './endpoint/index'; \ No newline at end of file diff --git a/src/ui_ng/lib/src/inline-alert/index.ts b/src/ui_ng/lib/src/inline-alert/index.ts new file mode 100644 index 000000000..3630add16 --- /dev/null +++ b/src/ui_ng/lib/src/inline-alert/index.ts @@ -0,0 +1,7 @@ +import { Type } from '@angular/core'; + +import { InlineAlertComponent } from './inline-alert.component'; + +export const INLINE_ALERT_DIRECTIVES: Type[] = [ + InlineAlertComponent +]; \ No newline at end of file diff --git a/src/ui_ng/lib/src/inline-alert/inline-alert.component.css.ts b/src/ui_ng/lib/src/inline-alert/inline-alert.component.css.ts new file mode 100644 index 000000000..089b188c8 --- /dev/null +++ b/src/ui_ng/lib/src/inline-alert/inline-alert.component.css.ts @@ -0,0 +1,10 @@ +export const INLINE_ALERT_STYLE: string = ` + .alert-text-blink { + color: red; + font-weight: bolder; + } + .alert-btn-link { + padding: 0px !important; + min-width: 30px !important; + } +`; \ No newline at end of file diff --git a/src/ui_ng/lib/src/inline-alert/inline-alert.component.html.ts b/src/ui_ng/lib/src/inline-alert/inline-alert.component.html.ts new file mode 100644 index 000000000..2a07c3359 --- /dev/null +++ b/src/ui_ng/lib/src/inline-alert/inline-alert.component.html.ts @@ -0,0 +1,13 @@ +export const INLINE_ALERT_TEMPLATE: string = ` + +
+ + {{errorMessage}} + +
+ + +
+
+
+`; \ No newline at end of file diff --git a/src/ui_ng/lib/src/inline-alert/inline-alert.component.ts b/src/ui_ng/lib/src/inline-alert/inline-alert.component.ts new file mode 100644 index 000000000..db0378d31 --- /dev/null +++ b/src/ui_ng/lib/src/inline-alert/inline-alert.component.ts @@ -0,0 +1,99 @@ +// Copyright (c) 2017 VMware, Inc. 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. +import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; + +import { errorHandler } from '../shared/shared.utils'; +import { Observable } from 'rxjs/Rx'; +import { Subscription } from "rxjs"; + +import { INLINE_ALERT_STYLE } from './inline-alert.component.css'; +import { INLINE_ALERT_TEMPLATE } from './inline-alert.component.html'; + +@Component({ + selector: 'inline-alert', + template: INLINE_ALERT_TEMPLATE, + styles: [ INLINE_ALERT_STYLE ] +}) +export class InlineAlertComponent { + inlineAlertType: string = 'alert-danger'; + inlineAlertClosable: boolean = false; + alertClose: boolean = true; + displayedText: string = ""; + showCancelAction: boolean = false; + useAppLevelStyle: boolean = false; + timer: Subscription | null = null; + count: number = 0; + blinking: boolean = false; + + @Output() confirmEvt = new EventEmitter(); + + constructor(private translate: TranslateService) { } + + public get errorMessage(): string { + return this.displayedText; + } + + //Show error message inline + public showInlineError(error: any): void { + this.displayedText = errorHandler(error); + if (this.displayedText) { + this.translate.get(this.displayedText).subscribe((res: string) => this.displayedText = res); + } + + this.inlineAlertType = 'alert-danger'; + this.showCancelAction = false; + this.inlineAlertClosable = true; + this.alertClose = false; + this.useAppLevelStyle = false; + } + + //Show confirmation info with action button + public showInlineConfirmation(warning: any): void { + this.displayedText = ""; + if (warning && warning.message) { + this.translate.get(warning.message).subscribe((res: string) => this.displayedText = res); + } + this.inlineAlertType = 'alert-warning'; + this.showCancelAction = true; + this.inlineAlertClosable = false; + this.alertClose = false; + this.useAppLevelStyle = false; + } + + //Show inline sccess info + public showInlineSuccess(info: any): void { + this.displayedText = ""; + if (info && info.message) { + this.translate.get(info.message).subscribe((res: string) => this.displayedText = res); + } + this.inlineAlertType = 'alert-success'; + this.showCancelAction = false; + this.inlineAlertClosable = true; + this.alertClose = false; + this.useAppLevelStyle = false; + } + + //Close alert + public close(): void { + this.alertClose = true; + } + + public blink() { + } + + confirmCancel(): void { + this.confirmEvt.emit(true); + } +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/service/endpoint.service.spec.ts b/src/ui_ng/lib/src/service/endpoint.service.spec.ts index 37890bb26..590d3a28c 100644 --- a/src/ui_ng/lib/src/service/endpoint.service.spec.ts +++ b/src/ui_ng/lib/src/service/endpoint.service.spec.ts @@ -1,10 +1,13 @@ import { TestBed, inject } from '@angular/core/testing'; - +import { SharedModule } from '../shared/shared.module'; import { EndpointService, EndpointDefaultService } from './endpoint.service'; describe('EndpointService', () => { beforeEach(() => { TestBed.configureTestingModule({ + imports: [ + SharedModule + ], providers: [ EndpointDefaultService, { diff --git a/src/ui_ng/lib/src/service/endpoint.service.ts b/src/ui_ng/lib/src/service/endpoint.service.ts index 9ab6fc957..444d19070 100644 --- a/src/ui_ng/lib/src/service/endpoint.service.ts +++ b/src/ui_ng/lib/src/service/endpoint.service.ts @@ -1,7 +1,8 @@ import { Observable } from 'rxjs/Observable'; import { RequestQueryParams } from './RequestQueryParams'; -import { Endpoint } from './interface'; +import { Endpoint, ReplicationRule } from './interface'; import { Injectable } from "@angular/core"; +import { Http } from '@angular/http'; import 'rxjs/add/observable/of'; /** @@ -80,6 +81,15 @@ export abstract class EndpointService { * @memberOf EndpointService */ abstract pingEndpoint(endpoint: Endpoint): Observable | Promise | any; + + /** + * Check endpoint whether in used with specific replication rule. + * + * @abstract + * @param {{number | string}} endpointId + * @returns {{Observable | any}} + */ + abstract getEndpointWithReplicationRules(endpointId: number | string): Observable | Promise | any; } /** @@ -91,28 +101,71 @@ export abstract class EndpointService { */ @Injectable() export class EndpointDefaultService extends EndpointService { + + constructor(private http: Http){ + super(); + } + public getEndpoints(endpointName?: string, queryParams?: RequestQueryParams): Observable | Promise | Endpoint[] { - return Observable.of([]); + return this.http + .get(`/api/targets?name=${endpointName}`) + .toPromise() + .then(response=>response.json()) + .catch(error=>Promise.reject(error)); } public getEndpoint(endpointId: number | string): Observable | Promise | Endpoint { - return Observable.of({}); + return this.http + .get(`/api/targets/${endpointId}`) + .toPromise() + .then(response=>response.json() as Endpoint) + .catch(error=>Promise.reject(error)); } public createEndpoint(endpoint: Endpoint): Observable | Promise | any { - return Observable.of({}); + return this.http + .post(`/api/targets`, JSON.stringify(endpoint)) + .toPromise() + .then(response=>response.status) + .catch(error=>Promise.reject(error)); } public updateEndpoint(endpointId: number | string, endpoint: Endpoint): Observable | Promise | any { - return Observable.of({}); + return this.http + .put(`/api/targets/${endpointId}`, JSON.stringify(endpoint)) + .toPromise() + .then(response=>response.status) + .catch(error=>Promise.reject(error)); } public deleteEndpoint(endpointId: number | string): Observable | Promise | any { - return Observable.of({}); + return this.http + .delete(`/api/targets/${endpointId}`) + .toPromise() + .then(response=>response.status) + .catch(error=>Promise.reject(error)); } public pingEndpoint(endpoint: Endpoint): Observable | Promise | any { - return Observable.of({}); + if(endpoint.id) { + return this.http + .post(`/api/targets/${endpoint.id}/ping`, {}) + .toPromise() + .then(response=>response.status) + .catch(error=>Promise.reject(error)); + } + return this.http + .post(`/api/targets/ping`, endpoint) + .toPromise() + .then(response=>response.status) + .catch(error=>Observable.throw(error)); } -} + public getEndpointWithReplicationRules(endpointId: number | string): Observable | Promise | any { + return this.http + .get(`/api/targets/${endpointId}/policies`) + .toPromise() + .then(response=>response.json() as ReplicationRule[]) + .catch(error=>Promise.reject(error)); + } +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/service/interface.ts b/src/ui_ng/lib/src/service/interface.ts index 7c21fef29..889a00652 100644 --- a/src/ui_ng/lib/src/service/interface.ts +++ b/src/ui_ng/lib/src/service/interface.ts @@ -72,7 +72,13 @@ export interface Tag extends Base { * @interface Endpoint * @extends {Base} */ -export interface Endpoint extends Base { } +export interface Endpoint extends Base { + endpoint: string; + name: string; + username: string; + password: string; + type: number; +} /** * Interface for replication rule. diff --git a/src/ui_ng/lib/src/service/replication.service.ts b/src/ui_ng/lib/src/service/replication.service.ts index 249ba4caa..6c2b52222 100644 --- a/src/ui_ng/lib/src/service/replication.service.ts +++ b/src/ui_ng/lib/src/service/replication.service.ts @@ -114,7 +114,6 @@ export abstract class ReplicationService { * @memberOf ReplicationService */ abstract getJobs(ruleId: number | string, queryParams?: RequestQueryParams): Observable | Promise | ReplicationJob[]; - } /** diff --git a/src/ui_ng/lib/src/shared/shared.const.ts b/src/ui_ng/lib/src/shared/shared.const.ts new file mode 100644 index 000000000..9a60f623f --- /dev/null +++ b/src/ui_ng/lib/src/shared/shared.const.ts @@ -0,0 +1,68 @@ +// Copyright (c) 2017 VMware, Inc. 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 supportedLangs = ['en-us', 'zh-cn', 'es-es']; +export const enLang = "en-us"; +export const languageNames = { + "en-us": "English", + "zh-cn": "中文简体", + "es-es": "Español" +}; +export const enum AlertType { + DANGER, WARNING, INFO, SUCCESS +}; + +export const dismissInterval = 10 * 1000; +export const httpStatusCode = { + "Unauthorized": 401, + "Forbidden": 403 +}; +export const enum ConfirmationTargets { + EMPTY, + PROJECT, + PROJECT_MEMBER, + USER, + POLICY, + TOGGLE_CONFIRM, + TARGET, + REPOSITORY, + TAG, + CONFIG, + CONFIG_ROUTE, + CONFIG_TAB +}; + +export const enum ActionType { + ADD_NEW, EDIT +}; + +export const ListMode = { + READONLY: "readonly", + FULL: "full" +}; + +export const CommonRoutes = { + SIGN_IN: "/sign-in", + EMBEDDED_SIGN_IN: "/harbor/sign-in", + SIGN_UP: "/sign-in?sign_up=true", + EMBEDDED_SIGN_UP: "/harbor/sign-in?sign_up=true", + HARBOR_ROOT: "/harbor", + HARBOR_DEFAULT: "/harbor/projects" +}; + +export const enum ConfirmationState { + NA, CONFIRMED, CANCEL +} +export const enum ConfirmationButtons { + CONFIRM_CANCEL, YES_NO, DELETE_CANCEL, CLOSE +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/shared/shared.module.ts b/src/ui_ng/lib/src/shared/shared.module.ts index 70f00d7ea..95108e1d3 100644 --- a/src/ui_ng/lib/src/shared/shared.module.ts +++ b/src/ui_ng/lib/src/shared/shared.module.ts @@ -1,14 +1,14 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { HttpModule } from '@angular/http'; +import { HttpModule, Http } from '@angular/http'; import { ClarityModule } from 'clarity-angular'; import { FormsModule } from '@angular/forms'; import { TranslateModule, TranslateLoader, TranslateService, MissingTranslationHandler } from "@ngx-translate/core"; import { MyMissingTranslationHandler } from '../i18n/missing-trans.handler'; import { TranslateHttpLoader } from '@ngx-translate/http-loader'; -import { Http } from '@angular/http'; import { TranslatorJsonLoader } from '../i18n/local-json.loader'; + /*export function HttpLoaderFactory(http: Http) { return new TranslateHttpLoader(http, 'i18n/lang/', '-lang.json'); }*/ diff --git a/src/ui_ng/lib/src/shared/shared.utils.ts b/src/ui_ng/lib/src/shared/shared.utils.ts new file mode 100644 index 000000000..fe0550ebe --- /dev/null +++ b/src/ui_ng/lib/src/shared/shared.utils.ts @@ -0,0 +1,49 @@ +// Copyright (c) 2017 VMware, Inc. 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. +import { NgForm } from '@angular/forms'; +import { httpStatusCode, AlertType } from './shared.const'; +/** + * To handle the error message body + * + * @export + * @returns {string} + */ +export const errorHandler = function (error: any): string { + if (!error) { + return "UNKNOWN_ERROR"; + } + if (!(error.statusCode || error.status)) { + //treat as string message + return '' + error; + } else { + switch (error.statusCode || error.status) { + case 400: + return "BAD_REQUEST_ERROR"; + case 401: + return "UNAUTHORIZED_ERROR"; + case 403: + return "FORBIDDEN_ERROR"; + case 404: + return "NOT_FOUND_ERROR"; + case 412: + return "PRECONDITION_FAILED"; + case 409: + return "CONFLICT_ERROR"; + case 500: + return "SERVER_ERROR"; + default: + return "UNKNOWN_ERROR"; + } + } +} \ No newline at end of file