Update for shareable endpoint.

This commit is contained in:
kunw 2017-05-12 14:24:23 +08:00
parent 5071dcf304
commit 900854128a
7 changed files with 242 additions and 144 deletions

View File

@ -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,8 +26,6 @@ describe('CreateEditEndpointComponent (inline template)', () => {
let comp: CreateEditEndpointComponent;
let fixture: ComponentFixture<CreateEditEndpointComponent>;
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(()=>{
it('should get endpoint and open modal', async(()=>{
fixture.detectChanges();
comp.openCreateEditTarget(true, 1);
comp.createEditDestinationOpened = false;
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');
});
});

View File

@ -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,48 +166,42 @@ export class CreateEditEndpointComponent implements AfterViewChecked {
onSubmit() {
switch(this.actionType) {
case ActionType.ADD_NEW:
this.addEndpoint();
break;
case ActionType.EDIT:
this.updateEndpoint();
break;
}
}
addEndpoint() {
toPromise<number>(this.endpointService
.createEndpoint(this.target))
.then(
response=>{
this.errorHandler.info('DESTINATION.CREATED_SUCCESS');
this.translateService.get('DESTINATION.CREATED_SUCCESS')
.subscribe(res=>this.errorHandler.info(res));
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';
}
let errorMessageKey = this.handleErrorMessageKey(error.status);
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:
}
updateEndpoint() {
if(!(this.targetNameHasChanged || this.endpointHasChanged)) {
this.createEditDestinationOpened = false;
return;
}
let payload: Endpoint = Object.assign({}, this.initEndpoint);
let payload: Endpoint = this.initEndpoint;
if(this.targetNameHasChanged) {
payload.name = this.target.name;
}
@ -223,37 +215,31 @@ export class CreateEditEndpointComponent implements AfterViewChecked {
.updateEndpoint(this.target.id, payload))
.then(
response=>{
this.errorHandler.info('DESTINATION.UPDATED_SUCCESS');
this.translateService.get('DESTINATION.UPDATED_SUCCESS')
.subscribe(res=>this.errorHandler.info(res));
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';
}
let errorMessageKey = this.handleErrorMessageKey(error.status);
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;
}
handleErrorMessageKey(status: number): string {
switch(status) {
case 409:this
return 'DESTINATION.CONFLICT_NAME';
case 400:
return 'DESTINATION.INVALID_NAME';
default:
return 'UNKNOWN_ERROR';
}
}

View File

@ -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<EndpointComponent>;
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,
@ -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');
});
}));
});

View File

@ -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,10 +99,7 @@ export class EndpointComponent implements OnInit {
}
}
cancelDeletion(message: ConfirmationAcknowledgement) {
console.log('Received message from cancelAction:' + JSON.stringify(message));
}
cancelDeletion(message: ConfirmationAcknowledgement) {}
ngOnInit(): void {
this.targetName = '';
@ -142,19 +145,12 @@ export class EndpointComponent implements OnInit {
editTarget(target: Endpoint) {
if (target) {
let editable = true;
toPromise<ReplicationRule[]>(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);

View File

@ -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

View File

@ -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<Endpoint[]> | Promise<Endpoint[]> | 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}`)
.get(requestUrl, buildHttpRequestOptions(queryParams))
.toPromise()
.then(response=>response.json())
.catch(error=>Promise.reject(error));
}
public getEndpoint(endpointId: number | string): Observable<Endpoint> | Promise<Endpoint> | Endpoint {
if(!endpointId || endpointId <= 0) {
return Promise.reject('Bad request argument.');
}
let requestUrl: string = `${this._endpointUrl}/${endpointId}`;
return this.http
.get(`/api/targets/${endpointId}`)
.get(requestUrl)
.toPromise()
.then(response=>response.json() as Endpoint)
.catch(error=>Promise.reject(error));
}
public createEndpoint(endpoint: Endpoint): Observable<any> | Promise<any> | 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<any> | Promise<any> | 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<any> | Promise<any> | 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<any> | Promise<any> | 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<any> | Promise<any> | 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));

View File

@ -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<T> 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);
}
}