From 900854128a15587e12e29077188f4988202ded1f Mon Sep 17 00:00:00 2001 From: kunw Date: Fri, 12 May 2017 14:24:23 +0800 Subject: [PATCH] Update for shareable endpoint. --- .../create-edit-endpoint.component.spec.ts | 27 ++- .../create-edit-endpoint.component.ts | 168 ++++++++---------- .../src/endpoint/endpoint.component.spec.ts | 57 +++++- .../lib/src/endpoint/endpoint.component.ts | 24 ++- .../lib/src/service/endpoint.service.spec.ts | 11 ++ src/ui_ng/lib/src/service/endpoint.service.ts | 80 +++++++-- src/ui_ng/lib/src/utils.ts | 19 ++ 7 files changed, 242 insertions(+), 144 deletions(-) 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 index c39f19cf9..c33624885 100644 --- 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 @@ -1,9 +1,8 @@ import { ComponentFixture, TestBed, async, fakeAsync, tick } from '@angular/core/testing'; +import { NoopAnimationsModule } from "@angular/platform-browser/animations"; 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'; @@ -27,9 +26,7 @@ describe('CreateEditEndpointComponent (inline template)', () => { let comp: CreateEditEndpointComponent; let fixture: ComponentFixture; - let de: DebugElement; - let el: HTMLElement; - + let config: IServiceConfig = { systemInfoEndpoint: '/api/endpoints/testing' }; @@ -40,7 +37,10 @@ describe('CreateEditEndpointComponent (inline template)', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ SharedModule ], + imports: [ + SharedModule, + NoopAnimationsModule + ], declarations: [ FilterComponent, CreateEditEndpointComponent, @@ -48,8 +48,7 @@ describe('CreateEditEndpointComponent (inline template)', () => { providers: [ ErrorHandler, { provide: SERVICE_CONFIG, useValue: config }, - { provide: EndpointService, useClass: EndpointDefaultService }, - { provide: TranslateService, useClass: TranslateService} + { provide: EndpointService, useClass: EndpointDefaultService } ] }); })); @@ -61,6 +60,9 @@ describe('CreateEditEndpointComponent (inline template)', () => { endpointService = fixture.debugElement.injector.get(EndpointService); spy = spyOn(endpointService, 'getEndpoint').and.returnValue(Promise.resolve(mockData)); fixture.detectChanges(); + + comp.openCreateEditTarget(true, 1); + fixture.detectChanges(); }); it('should be created', () => { @@ -70,18 +72,14 @@ describe('CreateEditEndpointComponent (inline template)', () => { 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; + it('should get endpoint and open modal', async(()=>{ + fixture.detectChanges(); fixture.whenStable().then(()=>{ fixture.detectChanges(); expect(comp.target.name).toEqual('target_01'); @@ -92,5 +90,4 @@ describe('CreateEditEndpointComponent (inline template)', () => { 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 index c598c3183..4f51cc911 100644 --- 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 @@ -50,8 +50,8 @@ export class CreateEditEndpointComponent implements AfterViewChecked { actionType: ActionType; - target: Endpoint = Object.assign({}, this.initEndpoint); - initVal: Endpoint = Object.assign({}, this.initEndpoint); + target: Endpoint = this.initEndpoint; + initVal: Endpoint = this.initEndpoint; targetForm: NgForm; @@ -89,7 +89,7 @@ export class CreateEditEndpointComponent implements AfterViewChecked { openCreateEditTarget(editable: boolean, targetId?: number) { - this.target = Object.assign({}, this.initEndpoint); + this.target = this.initEndpoint; this.editable = editable; this.createEditDestinationOpened = true; this.hasChanged = false; @@ -113,7 +113,6 @@ export class CreateEditEndpointComponent implements AfterViewChecked { this.initVal.username = this.target.username; this.initVal.password = FAKE_PASSWORD; this.target.password = this.initVal.password; - }) .catch(error=>this.errorHandler.error(error)); } else { @@ -127,7 +126,7 @@ export class CreateEditEndpointComponent implements AfterViewChecked { this.pingStatus = true; this.testOngoing = !this.testOngoing; - let payload: Endpoint = Object.assign({}, this.initEndpoint);; + let payload: Endpoint = this.initEndpoint; if(this.endpointHasChanged) { payload.endpoint = this.target.endpoint; payload.username = this.target.username; @@ -148,7 +147,6 @@ export class CreateEditEndpointComponent implements AfterViewChecked { this.pingStatus = false; this.translateService.get('DESTINATION.TEST_CONNECTION_FAILURE').subscribe(res=>this.pingTestMessage=res); this.testOngoing = !this.testOngoing; - }); } @@ -168,95 +166,83 @@ export class CreateEditEndpointComponent implements AfterViewChecked { 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; + this.addEndpoint(); + 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; + this.updateEndpoint(); + break; } } + addEndpoint() { + toPromise(this.endpointService + .createEndpoint(this.target)) + .then( + response=>{ + this.translateService.get('DESTINATION.CREATED_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.errorHandler.error(res); + }); + } + ); + } + + updateEndpoint() { + if(!(this.targetNameHasChanged || this.endpointHasChanged)) { + this.createEditDestinationOpened = false; + return; + } + let payload: Endpoint = 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.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.errorHandler.error(res); + }); + } + ); + } + + 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'}); diff --git a/src/ui_ng/lib/src/endpoint/endpoint.component.spec.ts b/src/ui_ng/lib/src/endpoint/endpoint.component.spec.ts index 27be4ef9c..2b7af9f78 100644 --- a/src/ui_ng/lib/src/endpoint/endpoint.component.spec.ts +++ b/src/ui_ng/lib/src/endpoint/endpoint.component.spec.ts @@ -1,5 +1,6 @@ -import { ComponentFixture, TestBed, async } from '@angular/core/testing'; +import { ComponentFixture, TestBed, async, fakeAsync, tick } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; +import { NoopAnimationsModule } from "@angular/platform-browser/animations"; import { DebugElement } from '@angular/core'; import { SharedModule } from '../shared/shared.module'; @@ -12,6 +13,9 @@ 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'; + +import { click } from '../utils'; + describe('EndpointComponent (inline template)', () => { let mockData: Endpoint[] = [ @@ -60,9 +64,6 @@ describe('EndpointComponent (inline template)', () => { let comp: EndpointComponent; let fixture: ComponentFixture; - let de: DebugElement; - let el: HTMLElement; - let config: IServiceConfig = { systemInfoEndpoint: '/api/endpoints/testing' }; @@ -73,7 +74,10 @@ describe('EndpointComponent (inline template)', () => { let spyOne: jasmine.Spy; beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ SharedModule ], + imports: [ + SharedModule, + NoopAnimationsModule + ], declarations: [ FilterComponent, ConfirmationDialogComponent, @@ -98,7 +102,7 @@ describe('EndpointComponent (inline template)', () => { spyOnRules = spyOn(endpointService, 'getEndpointWithReplicationRules').and.returnValue([]); spyOne = spyOn(endpointService, 'getEndpoint').and.returnValue(Promise.resolve(mockOne)); fixture.detectChanges(); - }); + }); it('should retrieve endpoint data', () => { fixture.detectChanges(); @@ -130,4 +134,45 @@ describe('EndpointComponent (inline template)', () => { }); })); + it('should render data', async(()=>{ + fixture.detectChanges(); + fixture.whenStable().then(()=>{ + fixture.detectChanges(); + let de: DebugElement = fixture.debugElement.query(By.css('datagrid-cell')); + expect(de).toBeTruthy(); + let el: HTMLElement = de.nativeElement; + expect(el.textContent).toEqual('target_01'); + }); + })); + + it('should open creation endpoint', async(()=>{ + fixture.detectChanges(); + fixture.whenStable().then(()=>{ + let de: DebugElement = fixture.debugElement.query(By.css('btn-link')); + expect(de).toBeTruthy(); + fixture.detectChanges(); + click(de); + fixture.detectChanges(); + let deInput: DebugElement = fixture.debugElement.query(By.css('input')); + expect(deInput).toBeTruthy(); + }); + })); + + + it('should open to edit existing endpoint', async(()=>{ + fixture.detectChanges(); + fixture.whenStable().then(()=>{ + let de: DebugElement = fixture.debugElement.query(del=>del.classes['action-item']); + expect(de).toBeTruthy(); + fixture.detectChanges(); + click(de); + fixture.detectChanges(); + let deInput: DebugElement = fixture.debugElement.query(By.css('input')); + expect(deInput).toBeTruthy(); + let elInput: HTMLElement = deInput.nativeElement; + expect(elInput).toBeTruthy(); + expect(elInput.textContent).toEqual('target_01'); + }); + })); + }); \ 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 index b2555031d..721a49876 100644 --- a/src/ui_ng/lib/src/endpoint/endpoint.component.ts +++ b/src/ui_ng/lib/src/endpoint/endpoint.component.ts @@ -14,6 +14,9 @@ import { Component, OnInit, ViewChild, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; import { Endpoint, ReplicationRule } from '../service/interface'; import { EndpointService } from '../service/endpoint.service'; + +import { TranslateService } from '@ngx-translate/core'; + import { ErrorHandler } from '../error-handler/index'; import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message'; @@ -65,6 +68,7 @@ export class EndpointComponent implements OnInit { constructor( private endpointService: EndpointService, private errorHandler: ErrorHandler, + private translateService: TranslateService, private ref: ChangeDetectorRef) { let hnd = setInterval(()=>ref.markForCheck(), 100); setTimeout(()=>clearInterval(hnd), 1000); @@ -80,12 +84,14 @@ export class EndpointComponent implements OnInit { .deleteEndpoint(targetId)) .then( response => { - this.errorHandler.error('DESTINATION.DELETED_SUCCESS'); + this.translateService.get('DESTINATION.DELETED_SUCCESS') + .subscribe(res=>this.errorHandler.info(res)); this.reload(true); }).catch( error => { if(error && error.status === 412) { - this.errorHandler.error('DESTINATION.FAILED_TO_DELETE_TARGET_IN_USED'); + this.translateService.get('DESTINATION.FAILED_TO_DELETE_TARGET_IN_USED') + .subscribe(res=>this.errorHandler.error(res)); } else { this.errorHandler.error(error); } @@ -93,11 +99,8 @@ export class EndpointComponent implements OnInit { } } - cancelDeletion(message: ConfirmationAcknowledgement) { - console.log('Received message from cancelAction:' + JSON.stringify(message)); - } + cancelDeletion(message: ConfirmationAcknowledgement) {} - ngOnInit(): void { this.targetName = ''; this.retrieve(''); @@ -142,19 +145,12 @@ export class EndpointComponent implements OnInit { 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; - } - } + rules.forEach((rule)=>editable = (rule && rule.enabled !== 1)); } this.createEditEndpointComponent.openCreateEditTarget(editable, +target.id); let hnd = setInterval(()=>this.ref.markForCheck(), 100); 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 590d3a28c..48b2ada1e 100644 --- a/src/ui_ng/lib/src/service/endpoint.service.spec.ts +++ b/src/ui_ng/lib/src/service/endpoint.service.spec.ts @@ -1,8 +1,15 @@ import { TestBed, inject } from '@angular/core/testing'; import { SharedModule } from '../shared/shared.module'; import { EndpointService, EndpointDefaultService } from './endpoint.service'; +import { IServiceConfig, SERVICE_CONFIG } from '../service.config'; + describe('EndpointService', () => { + + let mockEndpoint:IServiceConfig = { + targetBaseEndpoint: '/api/endpoint/testing' + }; + beforeEach(() => { TestBed.configureTestingModule({ imports: [ @@ -10,6 +17,10 @@ describe('EndpointService', () => { ], providers: [ EndpointDefaultService, + { + provide: SERVICE_CONFIG, + useValue: mockEndpoint + }, { provide: EndpointService, useClass: EndpointDefaultService diff --git a/src/ui_ng/lib/src/service/endpoint.service.ts b/src/ui_ng/lib/src/service/endpoint.service.ts index 444d19070..f63bf7d96 100644 --- a/src/ui_ng/lib/src/service/endpoint.service.ts +++ b/src/ui_ng/lib/src/service/endpoint.service.ts @@ -1,10 +1,14 @@ import { Observable } from 'rxjs/Observable'; import { RequestQueryParams } from './RequestQueryParams'; import { Endpoint, ReplicationRule } from './interface'; -import { Injectable } from "@angular/core"; +import { Injectable, Inject } from "@angular/core"; import { Http } from '@angular/http'; import 'rxjs/add/observable/of'; +import { IServiceConfig, SERVICE_CONFIG } from '../service.config'; + +import { buildHttpRequestOptions } from '../utils'; + /** * Define the service methods to handle the endpoint related things. * @@ -102,68 +106,108 @@ export abstract class EndpointService { @Injectable() export class EndpointDefaultService extends EndpointService { - constructor(private http: Http){ + _endpointUrl: string; + + constructor( + @Inject(SERVICE_CONFIG) config: IServiceConfig, + private http: Http){ super(); + this._endpointUrl = config.targetBaseEndpoint ? config.targetBaseEndpoint : '/api/targets'; } public getEndpoints(endpointName?: string, queryParams?: RequestQueryParams): Observable | Promise | Endpoint[] { + if(!queryParams) { + queryParams = new RequestQueryParams(); + } + if(endpointName) { + queryParams.set('endpointName', endpointName); + } + let requestUrl: string = `${this._endpointUrl}`; return this.http - .get(`/api/targets?name=${endpointName}`) - .toPromise() - .then(response=>response.json()) - .catch(error=>Promise.reject(error)); + .get(requestUrl, buildHttpRequestOptions(queryParams)) + .toPromise() + .then(response=>response.json()) + .catch(error=>Promise.reject(error)); } public getEndpoint(endpointId: number | string): Observable | Promise | Endpoint { - return this.http - .get(`/api/targets/${endpointId}`) + if(!endpointId || endpointId <= 0) { + return Promise.reject('Bad request argument.'); + } + let requestUrl: string = `${this._endpointUrl}/${endpointId}`; + return this.http + .get(requestUrl) .toPromise() .then(response=>response.json() as Endpoint) .catch(error=>Promise.reject(error)); } public createEndpoint(endpoint: Endpoint): Observable | Promise | any { + if(!endpoint) { + return Promise.reject('Invalid endpoint.'); + } + let requestUrl: string = `${this._endpointUrl}`; return this.http - .post(`/api/targets`, JSON.stringify(endpoint)) + .post(requestUrl, JSON.stringify(endpoint)) .toPromise() .then(response=>response.status) .catch(error=>Promise.reject(error)); } public updateEndpoint(endpointId: number | string, endpoint: Endpoint): Observable | Promise | any { + if(!endpointId || endpointId <= 0) { + return Promise.reject('Bad request argument.'); + } + if(!endpoint) { + return Promise.reject('Invalid endpoint.'); + } + let requestUrl: string = `${this._endpointUrl}/${endpointId}`; return this.http - .put(`/api/targets/${endpointId}`, JSON.stringify(endpoint)) + .put(requestUrl, JSON.stringify(endpoint)) .toPromise() .then(response=>response.status) .catch(error=>Promise.reject(error)); } public deleteEndpoint(endpointId: number | string): Observable | Promise | any { + if(!endpointId || endpointId <= 0) { + return Promise.reject('Bad request argument.'); + } + let requestUrl: string = `${this._endpointUrl}/${endpointId}`; return this.http - .delete(`/api/targets/${endpointId}`) + .delete(requestUrl) .toPromise() .then(response=>response.status) .catch(error=>Promise.reject(error)); } public pingEndpoint(endpoint: Endpoint): Observable | Promise | any { + if(!endpoint) { + return Promise.reject('Invalid endpoint.'); + } + let requestUrl: string = `${this._endpointUrl}/ping`; if(endpoint.id) { return this.http - .post(`/api/targets/${endpoint.id}/ping`, {}) + .post(requestUrl, {}) + .toPromise() + .then(response=>response.status) + .catch(error=>Promise.reject(error)); + } else { + return this.http + .post(requestUrl, endpoint) .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 { + if(!endpointId || endpointId <= 0) { + return Promise.reject('Bad request argument.'); + } + let requestUrl: string = `${this._endpointUrl}/${endpointId}/policies`; return this.http - .get(`/api/targets/${endpointId}/policies`) + .get(requestUrl) .toPromise() .then(response=>response.json() as ReplicationRule[]) .catch(error=>Promise.reject(error)); diff --git a/src/ui_ng/lib/src/utils.ts b/src/ui_ng/lib/src/utils.ts index 85dcce116..9b0b7067e 100644 --- a/src/ui_ng/lib/src/utils.ts +++ b/src/ui_ng/lib/src/utils.ts @@ -2,6 +2,7 @@ import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/toPromise'; import { RequestOptions, Headers } from '@angular/http'; import { RequestQueryParams } from './service/RequestQueryParams'; +import { DebugElement } from '@angular/core'; /** * Convert the different async channels to the Promise type. @@ -67,3 +68,21 @@ export function buildHttpRequestOptions(params: RequestQueryParams): RequestOpti return reqOptions; } + + + +/** Button events to pass to `DebugElement.triggerEventHandler` for RouterLink event handler */ +export const ButtonClickEvents = { + left: { button: 0 }, + right: { button: 2 } +}; + + +/** Simulate element click. Defaults to mouse left-button click event. */ +export function click(el: DebugElement | HTMLElement, eventObj: any = ButtonClickEvents.left): void { + if (el instanceof HTMLElement) { + el.click(); + } else { + el.triggerEventHandler('click', eventObj); + } +} \ No newline at end of file